###### 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 <= mainTank, additionalTank <= 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 <= nums.length <= 10<sup>5</sup></code></li>
<li><code>1 <= nums[i] <= 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 <= nums.length <= 14</code></li>
<li><code>1 <= nums[i] <= 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 <= cost.length <= 500</code></li>
<li><code>cost.length == time.length</code></li>
<li><code>1 <= cost[i] <= 10<sup>6</sup></code></li>
<li><code>1 <= time[i] <= 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];
}
};
```