LeetCode #188場周賽題解
阿新 • • 發佈:2020-05-10
[A題連結](https://leetcode-cn.com/problems/build-an-array-with-stack-operations/)
給你一個目標陣列 target 和一個整數 n。每次迭代,需要從 list = {1,2,3..., n} 中依序讀取一個數字。
請使用下述操作來構建目標陣列 target :
* Push:從 list 中讀取一個新元素, 並將其推入陣列中。
* Pop:刪除陣列中的最後一個元素。
* 如果目標陣列構建完成,就停止讀取更多元素。
題目資料保證目標陣列嚴格遞增,並且只包含 1 到 n 之間的數字。
請返回構建目標陣列所用的操作序列。
題目資料保證答案是唯一的。
**示例 1**
```pascal
輸入:target = [1,3], n = 3
輸出:["Push","Push","Pop","Push"]
解釋:
讀取 1 並自動推入陣列 -> [1]
讀取 2 並自動推入陣列,然後刪除它 -> [1]
讀取 3 並自動推入陣列 -> [1,3]
```
**示例 2:**
```pascal
輸入:target = [1,2,3], n = 3
輸出:["Push","Push","Push"]
```
**示例 3:**
```pascal
輸入:target = [1,2], n = 4
輸出:["Push","Push"]
解釋:只需要讀取前 2 個數字就可以停止。
```
**提示:**
- `1 <= target.length <= 100`
- `1 <= target[i] <= 100`
- `1 <= n <= 100`
- `target` 是嚴格遞增的
```cpp
class Solution {
public:
//正常判斷是否需要在target陣列中儲存,同時如果足夠了即可break
vector buildArray(vector& target, int n) {
vectorv;
int len = target.size();
int j = 0;
for(int i = 1;i <=n ;++i){
if(j == len)break;
if(i == target[j]){
v.push_back("Push");
++j;
}
else{
v.push_back("Push");
v.push_back("Pop");
}
}
return v;
}
};
```
[B題](https://leetcode-cn.com/problems/count-triplets-that-can-form-two-arrays-of-equal-xor/)
給你一個整數陣列 arr 。
現需要從陣列中取三個下標 i、j 和 k ,其中 (0 <= i < j <= k < arr.length) 。
a 和 b 定義如下:
* a = arr[i] ^ arr[i + 1] ^ ... ^ arr[j - 1]
* b = arr[j] ^ arr[j + 1] ^ ... ^ arr[k]
注意:^ 表示 按位異或 操作。
請返回能夠令 `a == b` 成立的三元組$ (i, j , k)$ 的數目。
**示例 1:**
```pascal
輸入:arr = [2,3,1,6,7]
輸出:4
解釋:滿足題意的三元組分別是 (0,1,2), (0,2,2), (2,3,4) 以及 (2,4,4)
```
**示例2:**
```pascal
輸入:arr = [1,1,1,1,1]
輸出:10
```
**示例 3:**
```pascal
輸入:arr = [2,3]
輸出:0
```
**思路:**
arr[i]^...^arr[j-1]的異或結果可以轉化為(arr[0]^...^arr[j-1])^(arr[0]^...^arr[i-1]),因為相同的值異或為0,而異或一個0是不影響原結果的。因此事先計算出會用到的異或結果,用陣列dp儲存。
```cpp
class Solution {
public:
int countTriplets(vector& a) {
int n = a.size();
vector s(n+1);
for (int i = 1; i <= n; ++ i)
s[i] = s[i-1]^a[i-1];
int ret = 0;
for (int i = 1; i <= n; ++ i)
for (int j = i+1; j <= n; ++ j)
for (int k = j; k <= n; ++ k)
{
if ((s[j-1]^s[i-1]) == (s[k]^s[j-1])) ret ++;
}
return ret;
}
};
```
[C題](https://leetcode-cn.com/problems/minimum-time-to-collect-all-apples-in-a-tree/) 給你一棵有 n 個節點的無向樹,節點編號為 0 到 n-1 ,它們中有一些節點有蘋果。通過樹上的一條邊,需要花費 1 秒鐘。你從 節點 0 出發,請你返回最少需要多少秒,可以收集到所有蘋果,並回到節點 0 。 無向樹的邊由 edges 給出,其中 edges[i] = [fromi, toi] ,表示有一條邊連線 from 和 toi 。除此以外,還有一個布林陣列 hasApple ,其中 hasApple[i] = true 代表節點 i 有一個蘋果,否則,節點 i 沒有蘋果。 **示例 1:** ![](https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2020/05/10/min_time_collect_apple_1.png) ```pascal 輸入:n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], hasApple = [false,false,true,false,true,true,false] 輸出:8 解釋:上圖展示了給定的樹,其中紅色節點表示有蘋果。一個能收集到所有蘋果的最優方案由綠色箭頭表示。 ``` **示例 2:** ![](https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2020/05/10/min_time_collect_apple_2.png) ```pascal 輸入:n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], hasApple = [false,false,true,false,false,true,false] 輸出:6 解釋:上圖展示了給定的樹,其中紅色節點表示有蘋果。一個能收集到所有蘋果的最優方案由綠色箭頭表示。 ``` **思路:** 只要那個節點是true,向上一直將父節點同化,那麼路徑就等於:每兩個連線(子、父都為true)的點的那條線x 2 後的和 ```cpp class Solution { public: int minTime(int n, vector>& edges, vector& hasApple) {
int i,res=0;
//若子節點為true,則父節點同化為true
for(i=edges.size()-1; i>=0; i--)
if(hasApple[edges[i][1]]==true)
hasApple[edges[i][0]]=true;
// 收集蘋果的路徑即為所有節點為true的拓撲圖的所有連線*2
for(i=0; i
[D題](https://leetcode-cn.com/problems/number-of-ways-of-cutting-a-pizza/)
給你一個 rows x cols 大小的矩形披薩和一個整數 k ,矩形包含兩種字元: 'A' (表示蘋果)和 '.' (表示空白格子)。你需要切披薩 k-1 次,得到 k 塊披薩並送給別人。
切披薩的每一刀,先要選擇是向垂直還是水平方向切,再在矩形的邊界上選一個切的位置,將披薩一分為二。如果垂直地切披薩,那麼需要把左邊的部分送給一個人,如果水平地切,那麼需要把上面的部分送給一個人。在切完最後一刀後,需要把剩下來的一塊送給最後一個人。
請你返回確保每一塊披薩包含 至少 一個蘋果的切披薩方案數。由於答案可能是個很大的數字,請你返回它對 10^9 + 7 取餘的結果。
**示例 1:**
![](https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2020/05/10/ways_to_cut_apple_1.png)
```pascal
輸入:pizza = ["A..","AAA","..."], k = 3
輸出:3
解釋:上圖展示了三種切披薩的方案。注意每一塊披薩都至少包含一個蘋果。
```
**示例 2:**
```pascal
輸入:pizza = ["A..","AA.","..."], k = 3
輸出:1
```
**示例 3:**
```pascal
輸入:pizza = ["A..","A..","..."], k = 1
輸出:1
```
* 1 <= rows, cols <= 50
* rows == pizza.length
* cols == pizza[i].length
* 1 <= k <= 10
* pizza 只包含字元 'A' 和 '.' 。
**思路:**
本題是統計切割方案數,一看就是使用dp,怎麼來思考呢?
首先我們考慮存在的狀態數:
毫無疑問,披薩被切成k塊肯定是狀態之一。
而如何表示當前剩餘部分的披薩呢?題中說把左邊和上邊給一個人,可以知道,右下部分總是會剩餘下來。
所以可以記錄左上角的位置來表示剩餘的披薩。
因此一個三維陣列可以做為dp陣列:dp[i][j][k]
i,j表示披薩剩餘部分的左上角,k表示當前披薩被切成k塊
初始狀態顯而易見,由於只有一塊,沒有切,左上角為(0,0),所以dp[0][0][1]=1
**狀態轉移**
知道初始狀態後,我們就要開始進行狀態轉移了~
首先讓我們來考慮怎麼從一塊變成兩塊
```cpp
披薩:
A..
AAA
...
```
由於我們知道可以水平切和垂直切,左上角為(i,j),一刀切下去,可以從k變成k+1
因此,我們可以窮舉每個狀態水平切和垂直切的所有切法,來得到k+1的狀態。
因為每切一次得到的剩餘披薩左上角都不同,所以不會出現重複。
首先水平切:
```cpp
左上角為(0,0),k=1
A.. A..
+++ AAA
AAA +++
... ...
剩餘部分:左上角為(1,0),k=2 剩餘部分:左上角為(2,0),k=2 (不合理)
```
有這兩種切法,很清楚看出來,第二種切法是不可以的,因為下面那一部分不存在A,不符合題意。
如何判斷剩餘和切出來的披薩存不存在A,我們先記下這個問題,後面會提到。
所以可以得到水平切的狀態轉移方程:
```cpp
記原來的左上角為(i,j),新的左上角為(x,y)
if(兩部分都存在A){
dp[x][y][k+1]+=dp[i][j][k]
}
```
垂直切是和水平切一樣的,就不說了。
**解決存在A的問題**
我們如何判斷切開後的兩塊披薩是否存在A呢?
方法1:直接暴力求解,我不知道會不會超時,我沒有試,有興趣可以寫一下。
方法2:利用數學知識,計算出來對應披薩塊中A的數量,假如數量大於0,則存在A
方法3:別的方法,假如有人願意分享更簡單的,可以在評論分享,大家一起進步
我使用方法2,所以就寫一下方法2:
用陣列num[i][j]表示以(0,0)為左上角,(i,j)為右下角的披薩塊中包含的A數量
```cpp
上例中:
num:
1 1 1
2 3 4
2 3 4
```
怎麼計算num陣列使用簡單的dp和數學知識就可以了,這裡就不再贅述。
通過num陣列和獲得披薩塊的左上角和右下角就可以輕易地算出A的個數:
```cpp
這個大家肯定都會,就舉個例子吧,就是簡單的數學關係:
例:計算以(1,0)為左上角,(2,2)為右下角的披薩塊A數目:
num[2][2]-num[0][2]-num[2][-1]+num[0][-1];
下標中出現-1的num值都用0代替:所以為4-1-0-0=3
```
看不懂的可以直接看程式碼如何計算A數目,一看就明白了
```cpp
#define ll long long int
class Solution {
public:
const ll mod=1e9+7;
int ways(vector& pizza, int k) {
int row=pizza.size(),col=pizza[0].length();
//計算num
vector> num(row,vector(col,0));
if(pizza[0][0]=='A') num[0][0]=1;
for(int i=1;i>> dp(row,vector>(col,vector(k+1,0)));
dp[0][0][1]=1;
//從k=2開始填充
for(int x=2;x<=k;x++){
for(int i=0;i>& num,int sr,int sc,int er,int ec){
int num1=0,num2=0,num3=0,res;
if(sr!=0 && sc!=0) num1=num[sr-1][sc-1];
if(sr!=0) num2=num[sr-1][ec];
if(sc!=0) num3=num[er][sc-1];
return num[er][ec]-num2-num3+num1>0;
[C題](https://leetcode-cn.com/problems/minimum-time-to-collect-all-apples-in-a-tree/) 給你一棵有 n 個節點的無向樹,節點編號為 0 到 n-1 ,它們中有一些節點有蘋果。通過樹上的一條邊,需要花費 1 秒鐘。你從 節點 0 出發,請你返回最少需要多少秒,可以收集到所有蘋果,並回到節點 0 。 無向樹的邊由 edges 給出,其中 edges[i] = [fromi, toi] ,表示有一條邊連線 from 和 toi 。除此以外,還有一個布林陣列 hasApple ,其中 hasApple[i] = true 代表節點 i 有一個蘋果,否則,節點 i 沒有蘋果。 **示例 1:** ![](https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2020/05/10/min_time_collect_apple_1.png) ```pascal 輸入:n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], hasApple = [false,false,true,false,true,true,false] 輸出:8 解釋:上圖展示了給定的樹,其中紅色節點表示有蘋果。一個能收集到所有蘋果的最優方案由綠色箭頭表示。 ``` **示例 2:** ![](https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2020/05/10/min_time_collect_apple_2.png) ```pascal 輸入:n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], hasApple = [false,false,true,false,false,true,false] 輸出:6 解釋:上圖展示了給定的樹,其中紅色節點表示有蘋果。一個能收集到所有蘋果的最優方案由綠色箭頭表示。 ``` **思路:** 只要那個節點是true,向上一直將父節點同化,那麼路徑就等於:每兩個連線(子、父都為true)的點的那條線x 2 後的和 ```cpp class Solution { public: int minTime(int n, vector