---
# System prepended metadata

title: Weekly Contest 350
tags: [Weekly Contest]

---

###### tags: `Weekly Contest`
# Weekly Contest 350

## [2739. Total Distance Traveled](https://leetcode.com/problems/total-distance-traveled/)(<font color=#00B8A3>Easy</font>)

限制 :

<ul>
<li><code>1 &lt;= mainTank, additionalTank &lt;= 100</code></li>
</ul>

### Solution
這題算是常見的題目，感覺不用講應該也會。(優化後的我倒是不會🥵)
最主要的點是要看的懂，它的主燃料箱每五公升，會從副燃料箱中抽一公升過去。
#### 時間複雜度: $O(n)$

#### 空間複雜度: $O(1)$


程式碼:

```c++=
class Solution {
public:
    int distanceTraveled(int mainTank, int additionalTank) {
        int result  = 0;
        while(mainTank >= 5)
        {
            mainTank -= 5;
            result+=50;
            if(additionalTank > 0)
            {
                additionalTank -=1;
                mainTank+=1;
            }
        }
        result+= mainTank*10;
        return result;
    }
};
// 我還是不知道這個的時間複雜度是多少
```


## [2740. Find the Value of the Partition](https://leetcode.com/problems/find-the-value-of-the-partition/)(<font color=#FFC011>Medium</font>)

限制 :

<ul>
<li><code>2 &lt;= nums.length &lt;= 10<sup>5</sup></code></li>
<li><code>1 &lt;= nums[i] &lt;= 10<sup>9</sup></code></li>
</ul>

### Solution

他要的是某兩個陣列當中分別最大的值與最小的值，然後相減出來的值要最小。
我把他理解成，有序陣列當中相鄰相間的值最小的，其原因在於我可以把比較小的值當作某個陣列的最大值，比較大的值當作另外一組陣列的最小值，相減得到的值就是所要的答案。
#### 時間複雜度: $O(n*logn)$

#### 空間複雜度: $O(1)$

程式碼:

```c++=
class Solution {
public:
    int findValueOfPartition(vector<int>& nums) {
        sort(nums.begin(),nums.end());

        int result = INT_MAX;

        for(int i=0;i< nums.size()-1;i++)
        {
            result = min(result, nums[i+1]- nums[i]);
        }

        return result;
    }
};
```


## [2741. Special Permutations](https://leetcode.com/problems/special-permutations/)(<font color=#FFC011>Medium</font>)

限制 :

<ul>
<li><code>2 &lt;= nums.length &lt;= 14</code></li>
<li><code>1 &lt;= nums[i] &lt;= 10<sup>9</sup></code></li>
</ul>

### Solution
參考[這篇](https://leetcode.com/problems/special-permutations/solutions/3650431/easy-bitmask-dp-with-explanation-of-overlapping-top-down-c-beats-100-100/)。

因為所有排列的數量有 $14!$，所以暴力列舉是行不通的。這時候就需要使用到 dynamic programming 的思想來解決。

首先因為 $n<=14$，所以我們可以使用 **bit mask** 來記錄數字被選擇的狀態。假設題目給定的數列是 $[3, 6, 8, 9]$，那我們會選擇用 $4$ 個bit去表示該數列的狀態，舉例來說像是 $1101$ 就代表 $[3, 6, 9]$、$0101$ 代表 $[6, 9]$。

令 $dp[mask][idx]$ 為從 $mask$ 這個狀態下，且數列的最後一個元素是 $nums[idx]$ 開始，最終能組合成的 *Special Permutations* 總共有幾種。

從這個定義來看，base case 是當 $mask$ 全為 1 的時候，代表所有數字都已經被選擇了且整個數列是一個 *Special Permutations*，這種情況應該要回傳 1。

那對於某一個狀態我們要怎麼遞進到下一個狀態呢?
只需要 check 當前mask為 0 的數是否符合 *Special Permutations* 的條件。當前狀態的解就等於所有符合條件的下一個狀態的解的加總。也就是:
$$
\forall i \ satisfy \ 
\begin{cases}
mask[i]=0\\
nums[idx]\ \%\ nums[i]=0\ or\ nums[i]\ \%\ nums[idx]=0 \\
\end{cases} \\
next\_mask_i := mask\ |\ 2^i \\
dp[mask][idx] := \sum_i dp[next\_mask_i][i]\\
$$
以此來遞迴求解。
那解答就是 $dp[100...000][0]+dp[010...000][1]+dp[001...000][2]+...+dp[000...001][n-1]$。
也可以說解答是 $dp[000...000][any]$，但因為這個狀態不是合法的狀態，畢竟沒有選任何數怎麼可能會有最後被選的數。所以只能取只有一個 1 的合法狀態來計算結果。
#### 時間複雜度: $O(n*2^n)$

#### 空間複雜度: $O(n*2^n)$

程式碼:

```c++=
class Solution {
public:
    const int MOD = 1e9+7;
    int n;
    int dp[1<<14][14];
    int helper(int prev, int mask, vector<int>& nums){
        if(prev != -1 && dp[mask][prev] != -1)
            return dp[mask][prev];
        if(mask == (1<<n)-1)
            return 1;

        int total = 0;
        for(int j=0;j<n;j++){
            if(mask&(1<<j))
                continue;
            if(prev == -1 || nums[prev] % nums[j] == 0 || nums[j] % nums[prev] == 0)
                total = (total + helper(j, mask | (1<<j), nums))%MOD;
        }
        if(prev != -1)
            return dp[mask][prev] = total;
        return total;
    }
    int specialPerm(vector<int>& nums) {
        n = nums.size();
        memset(dp, -1, sizeof dp);

        return helper(-1, 0, nums);
    }
};
```


## [2742. Painting the Walls](https://leetcode.com/problems/painting-the-walls/)(<font color=#FF375F>Hard</font>)

限制 :

<ul>
<li><code>1 &lt;= cost.length &lt;= 500</code></li>
<li><code>cost.length == time.length</code></li>
<li><code>1 &lt;= cost[i] &lt;= 10<sup>6</sup></code></li>
<li><code>1 &lt;= time[i] &lt;= 500</code></li>
</ul>

### Solution
跟背包問題類似，從最大化收益變成最小化成本，背包重量變成牆壁數量。

我們可以把 free printer 視為是 paid printer 的 bonus。假設我們用 paid printer 塗了第 $i$ 道牆，那等於我們花了 $cost[i]$ 塗了 $1+time[i]$ 道牆，前面的 1 是 paid printer 塗的，後面的 $time[i]$ 是 free printer 塗的。

由此可以導出 dp 式，令 $dp[j]$ 是塗 $j$ 道牆所需要的最小成本:
$$
\forall i \in [0, n-1]\\
dp[j] = min(dp[j], dp[max(0, j-time[i]-1)]+cost[i])
$$

$dp[n]$ 即是所求。
#### 時間複雜度: $O(n^2)$

#### 空間複雜度: $O(n)$

程式碼:

```c++=
class Solution {
public:
    int paintWalls(vector<int>& cost, vector<int>& time) {
        int dp[505], n = cost.size();
        for(int i=0;i<=n;i++)
            dp[i] = 1e9;
        dp[0] = 0;
        for(int i=0;i<n;i++){
            for(int j=n;j>0;j--){
                dp[j] = min(dp[j], dp[max(0, j-time[i]-1)]+cost[i]);
            }
        }
        return dp[n];
    }
};
```

