diff --git a/Binary_Search/1102.Path-With-Maximum-Minimum-Value/1102.Path-With-Maximum-Minimum-Value.cpp b/Binary_Search/1102.Path-With-Maximum-Minimum-Value/1102.Path-With-Maximum-Minimum-Value_v1.cpp similarity index 53% rename from Binary_Search/1102.Path-With-Maximum-Minimum-Value/1102.Path-With-Maximum-Minimum-Value.cpp rename to Binary_Search/1102.Path-With-Maximum-Minimum-Value/1102.Path-With-Maximum-Minimum-Value_v1.cpp index 687cb3831..e45f394ed 100644 --- a/Binary_Search/1102.Path-With-Maximum-Minimum-Value/1102.Path-With-Maximum-Minimum-Value.cpp +++ b/Binary_Search/1102.Path-With-Maximum-Minimum-Value/1102.Path-With-Maximum-Minimum-Value_v1.cpp @@ -1,23 +1,13 @@ class Solution { public: - int maximumMinimumPath(vector>& A) + int maximumMinimumPath(vector>& grid) { - int left = INT_MAX; - int right = INT_MIN; - int M = A.size(); - int N = A[0].size(); - for (int i=0; i> A, int K) + bool check(vector> grid, int K) { - if (A[0][0]>({{1,0},{-1,0},{0,1},{0,-1}}); - int M = A.size(); - int N = A[0].size(); + int M = grid.size(), N = grid[0].size(); queue>q; q.push({0,0}); - A[0][0] = -1; + vector>visited(M, vector(N)); + visited[0][0] = 1; while (q.size()>0) { @@ -47,19 +37,16 @@ class Solution { { int i = x+dir[k].first; int j = y+dir[k].second; - if (i<0||i>=M||j<0||j>=N) - continue; - if (A[i][j]==-1) - continue; - if (A[i][j]=M||j<0||j>=N) continue; + if (visited[i][j]) continue; + if (grid[i][j]; +class Solution { + vector>dir = {{1,0},{-1,0},{0,1},{0,-1}}; +public: + int maximumMinimumPath(vector>& grid) + { + int M = grid.size(); + int N = grid[0].size(); + priority_queuepq; + + pq.push({grid[0][0], 0,0}); + vector>visited(M, vector(N,0)); + vector>rets(M, vector(N,0)); + + while (pq.size()>0) + { + auto [d, x, y] = pq.top(); + pq.pop(); + if (visited[x][y]==1) continue; + rets[x][y] = d; + visited[x][y] = 1; + if (x==M-1 && y==N-1) + return d; + + for (int k=0; k<4; k++) + { + int i = x+dir[k].first; + int j = y+dir[k].second; + if (i<0||i>=M||j<0||j>=N) + continue; + pq.push({min(d, grid[i][j]), i, j}); + } + } + + return -1; + } +}; diff --git a/Binary_Search/1102.Path-With-Maximum-Minimum-Value/Readme.md b/Binary_Search/1102.Path-With-Maximum-Minimum-Value/Readme.md index 14679ddcf..b76823958 100644 --- a/Binary_Search/1102.Path-With-Maximum-Minimum-Value/Readme.md +++ b/Binary_Search/1102.Path-With-Maximum-Minimum-Value/Readme.md @@ -1,11 +1,11 @@ ### 1102.Path-With-Maximum-Minimum-Value -此题是二分法的非常精彩的应用. +#### 解法1:二分搜索 -我们想,如果这个maximum score是x,那么意味着存在一条路径,里面不能有任何小于x的格子.因此,如果给定x,我们尝试用BFS的方法从左上角走到右下角.如果能抵达,说明至少存在一条成功的路,他们所有的元素都不会小于x,而且x还可能有提升的空间.相反,如果不能走到,说明从左上到右下被一些列小于x的各自给阻断了,因此我们对于maximum score的预期必须下调,至少得小于x. +我们想,如果这个maximum score是x,那么意味着存在一条路径,里面不能有任何小于x的格子。因此,如果给定x,我们尝试用BFS的方法从左上角走到右下角,约定不能经过任何小于x的格子。如果能抵达,说明至少存在一条成功的路,其沿途元素都不会小于x,故x是一个可行解,我们可以调高对maximum score的预期。相反,如果不能走到,说明从左上到右下肯定会被一系列小于x的格子给阻断了,因此我们对于maximum score的预期必须下调,至少得小于x. 所以二分的策略就非常清楚了: -``` +```cpp while (left& batteries) { - LL left = 0, right = LLONG_MAX/2; - // sort(batteries.rbegin(), batteries.rend()); - + LL left = 0, right = LLONG_MAX/n; while (left < right) { LL mid = right-(right-left)/2; - if (checkOK(mid, batteries, n)) - left = mid; + if (checkOK(mid, n, batteries)) + left = mid; else - right = mid-1; + right = mid-1; } - return left; + return left; } - bool checkOK(LL T, vector&nums, int n) + bool checkOK(LL T, LL n, vector& batteries) { - int count = 0; - LL cur = 0; - for (int i=0; i= T) - { - count++; - cur-=T; - } - if (count>= n) + sum += min((LL)x, T); + if (sum>= T*n) return true; } return false; } }; + diff --git a/Divide_Conquer/1649.Create-Sorted-Array-through-Instructions/1649.Create-Sorted-Array-through-Instructions_DivideConque.cpp b/Divide_Conquer/1649.Create-Sorted-Array-through-Instructions/1649.Create-Sorted-Array-through-Instructions_DivideConque.cpp index 09ed2ea13..aa5bed503 100644 --- a/Divide_Conquer/1649.Create-Sorted-Array-through-Instructions/1649.Create-Sorted-Array-through-Instructions_DivideConque.cpp +++ b/Divide_Conquer/1649.Create-Sorted-Array-through-Instructions/1649.Create-Sorted-Array-through-Instructions_DivideConque.cpp @@ -35,37 +35,36 @@ class Solution { numSmaller[i] += iter-(sorted+a); } - int i=a, j=mid+1, p = 0; - while (i<=mid && j<=b) - { - if (sorted[i]<=sorted[j]) - { - temp[p] = sorted[i]; - i++; - } - else - { - temp[p] = sorted[j]; - j++; - } - p++; - } - while (i<=mid) - { - temp[p] = sorted[i]; - i++; - p++; - } - while (j<=b) - { - temp[p] = sorted[j]; - j++; - p++; - } - for (int i=0; i& nums, int lower, int upper) { @@ -26,34 +26,36 @@ class Solution { result+=p2-p1; } - int i=a, j=mid+1, p = 0; - while (i<=mid && j<=b) - { - if (nums[i]<=nums[j]) - { - temp[p] = nums[i]; - i++; - } - else - { - temp[p] = nums[j]; - j++; - } - p++; - } - while (i<=mid) - { - temp[p] = nums[i]; - i++; - p++; - } - while (j<=b) - { - temp[p] = nums[j]; - j++; - p++; - } - for (int i=0; i& slices) - { - vectorf(k+1,0); // f[i]: the maximum gain by the current round if we take i slices, and we do take the current slice. - vectorg(k+1,0); // g[i]: the maximum gain by the current round if we take i slices, and we do NOT take the current slice. + { + vectordp0(k+1); + vectordp1(k+1); for (int i=st; i<=en; i++) for (int j=min(k,i-st+1); j>=1; j--) { - g[j] = max(g[j], f[j]); - f[j] = g[j-1] + slices[i]; + dp0[j] = max(dp0[j], dp1[j]); + dp1[j] = dp0[j-1] + slices[i]; } - return max(f[k], g[k]); + return max(dp0[k], dp1[k]); } }; diff --git a/Dynamic_Programming/1388.Pizza-With-3n-Slices/Readme.md b/Dynamic_Programming/1388.Pizza-With-3n-Slices/Readme.md index 58c299be1..b4f6fb092 100644 --- a/Dynamic_Programming/1388.Pizza-With-3n-Slices/Readme.md +++ b/Dynamic_Programming/1388.Pizza-With-3n-Slices/Readme.md @@ -1,6 +1,6 @@ ### 1388.Pizza-With-3n-Slices -此题的条件和```213.House-Robber-II```非常相似:永远不能取相邻的两个元素;首尾元素认为是相邻的。此外,本题隐含着领一个条件:最多只能取n/3个元素。 +此题的条件和```213.House-Robber-II```非常相似:永远不能取相邻的两个元素;首尾元素认为是相邻的。此外,本题隐含着领一个条件:最多取n/3个元素(事实上肯定会取满n/3个)。 当然,我们需要验证一下,是不是任意的n/3个互不相邻的元素集合,都可以按照题目中的取数规则来实现。事实上是可以的。例如```1000101010000```,有13个元素。其中1代表我们打算取的数。我们永远先取较为外层的数(随之删去左右相邻的两个零):原序列可以得到```0010101000```,接下来```0101000```,接下来```1000```,最后```0```.虽然严格的证明不太容易,但是这个规律还是容易发现的。 diff --git a/Dynamic_Programming/2318.Number-of-Distinct-Roll-Sequences/2318.Number-of-Distinct-Roll-Sequences.cpp b/Dynamic_Programming/2318.Number-of-Distinct-Roll-Sequences/2318.Number-of-Distinct-Roll-Sequences.cpp new file mode 100644 index 000000000..9e55479c6 --- /dev/null +++ b/Dynamic_Programming/2318.Number-of-Distinct-Roll-Sequences/2318.Number-of-Distinct-Roll-Sequences.cpp @@ -0,0 +1,45 @@ +using LL = long long; +LL M = 1e9+7; +class Solution { + LL dp[10001][7][7]; +public: + int distinctSequences(int n) + { + if (n==1) return 6; + + LL count = 0; + for (int a=1; a<=6; a++) + for (int b=1; b<=6; b++) + { + if (a!=b && gcd(a,b)==1) + { + dp[2][a][b] = 1; + count++; + } + } + if (n==2) return count; + + LL ret = 0; + for (int i=3; i<=n; i++) + for (int a=1; a<=6; a++) + for (int b=1; b<=6; b++) + { + if (a==b) continue; + if (gcd(a,b)>1) continue; + + for (int x=1; x<=6; x++) + { + if (x!=b) + { + dp[i][a][b] += dp[i-1][x][a]; + dp[i][a][b] %= M; + } + } + + if (i==n) + ret = (ret + dp[i][a][b]) %M; + } + + return ret; + } +}; diff --git a/Dynamic_Programming/2318.Number-of-Distinct-Roll-Sequences/Readme.md b/Dynamic_Programming/2318.Number-of-Distinct-Roll-Sequences/Readme.md new file mode 100644 index 000000000..507701172 --- /dev/null +++ b/Dynamic_Programming/2318.Number-of-Distinct-Roll-Sequences/Readme.md @@ -0,0 +1,13 @@ +### 2318.Number-of-Distinct-Roll-Sequences + +我们在基于前i-1位的方案之上,考虑序列的第i位的填充时,思考能否填写某个数字d,需要的约束有:和前一位不能相等,和前一位必须互质,和前两位不能相等。可见,dp[i]关系到了前两位的具体方案。所以我们设计状态dp[i][a][b],表示前i位里最后两位数字分别是a和b的情况下,所有的合法方案数目。 + +接下来我们就很容易看出dp[i]与dp[i-1]之间的转移关系。因为i的最后两位是a和b,那么i-1的最后两位必然是某个数x和a。所以我们枚举所有合法的x使得dp[i]能将b接在dp[i-1]之后,要使得```xab```合法需要的条件是: +1. a和x不能相等 +2. b和x不能相等 +3. x和a必须互质 +满足这些条件的话,就意味着```dp[i][a][b] += dp[i-1][x][a]```,即将所有合法的dp[i-1][x][a]方案后面直接加上一个b。 + +有人会说,这里dp[i][a][b]没有考虑检查a是否和x之前的那个数字相同呀。事实上,dp[i]的合法性是建立在dp[i-1]的基础上的。如果dp[i-1][x][a]代表了合法的方案数目,那么自然就不会存在a与x之前的数字相同的问题。 + +在实际计算中,我们在更新所有dp[i][a][b]的过程中,可以通过计算ab本身的合法性,来提前跳过一些不合法的dp[i][a][b] diff --git a/Dynamic_Programming/2320.Count-Number-of-Ways-to-Place-Houses/2320.Count-Number-of-Ways-to-Place-Houses_v1.cpp b/Dynamic_Programming/2320.Count-Number-of-Ways-to-Place-Houses/2320.Count-Number-of-Ways-to-Place-Houses_v1.cpp new file mode 100644 index 000000000..34580f542 --- /dev/null +++ b/Dynamic_Programming/2320.Count-Number-of-Ways-to-Place-Houses/2320.Count-Number-of-Ways-to-Place-Houses_v1.cpp @@ -0,0 +1,23 @@ +using LL = long long; +LL M = 1e9+7; +class Solution { + LL dp[10001][2]; + // dp[i][0]: the # of plans so that there is no building at the i-th plot + // dp[i][1]: the # of plans so that there is a building at the i-th plot +public: + int countHousePlacements(int n) + { + dp[0][0] = 1; + dp[0][1] = 0; + + for (int i=1; i<=n; i++) + { + dp[i][0] = (dp[i-1][0] + dp[i-1][1])%M; + dp[i][1] = dp[i-1][0]; + } + + LL ret = (dp[n][0]+dp[n][1]) % M; + + return ret * ret % M; + } +}; diff --git a/Dynamic_Programming/2320.Count-Number-of-Ways-to-Place-Houses/2320.Count-Number-of-Ways-to-Place-Houses_v2.cpp b/Dynamic_Programming/2320.Count-Number-of-Ways-to-Place-Houses/2320.Count-Number-of-Ways-to-Place-Houses_v2.cpp new file mode 100644 index 000000000..ff32bab04 --- /dev/null +++ b/Dynamic_Programming/2320.Count-Number-of-Ways-to-Place-Houses/2320.Count-Number-of-Ways-to-Place-Houses_v2.cpp @@ -0,0 +1,22 @@ +using LL = long long; +LL M = 1e9+7; +class Solution { + LL dp[10001]; // dp[i]: the # of plans so that there is a building at the i-th plot +public: + int countHousePlacements(int n) + { + dp[0] = 0; + dp[1] = 1; + + for (int i=2; i<=n; i++) + { + dp[i] = (dp[i-1] + dp[i-2])%M; + } + + LL ret = 1; + for (int i=1; i<=n; i++) + ret = (ret+dp[i]) % M; + + return ret * ret % M; + } +}; diff --git a/Dynamic_Programming/2320.Count-Number-of-Ways-to-Place-Houses/2320.Count-Number-of-Ways-to-Place-Houses_v3.cpp b/Dynamic_Programming/2320.Count-Number-of-Ways-to-Place-Houses/2320.Count-Number-of-Ways-to-Place-Houses_v3.cpp new file mode 100644 index 000000000..9250a2a75 --- /dev/null +++ b/Dynamic_Programming/2320.Count-Number-of-Ways-to-Place-Houses/2320.Count-Number-of-Ways-to-Place-Houses_v3.cpp @@ -0,0 +1,27 @@ +using LL = long long; +LL M = 1e9+7; +class Solution { + unordered_mapmemo; +public: + int countHousePlacements(int n) + { + LL ret = 0; + for (int r=0; 2*r-1<=n; r++) + ret = (ret + C(n-r+1, r))%M; + return ret * ret % M; + } + + LL C(int x, int y) + { + if (x& nums1, vector& nums2) + { + return max(solve(nums1,nums2), solve(nums2,nums1)); + } + int solve(vector& nums1, vector& nums2) + { + int n = nums1.size(); + vectornums(n); + for (int i=0; i=0; i--) { diff --git a/Math/1017.Convert-to-Base--2/Readme.md b/Math/1017.Convert-to-Base--2/Readme.md index 3ebdc9708..c9bd4ac28 100644 --- a/Math/1017.Convert-to-Base--2/Readme.md +++ b/Math/1017.Convert-to-Base--2/Readme.md @@ -2,11 +2,11 @@ 本质上和求N的任何K进制的转化一样的做法。求得余数```r=N%K```作为当前最低位的数字,然后将```N=(N-r)/K```作为下一个循环的初始值直至为零。把所有的数字拼接起来倒序输出就是K进制的结果。 -特别注意,余数r必须是正数,也就是说无法除尽的时候,采用的是向下取整。比如说5/(-3),依据严格的数学定义,商是-2,余数是1. +特别注意,余数r必须是正数,也就是说无法除尽的时候,采用的是向下取整。比如说(-5)/(-3),依据严格的数学定义,商是2,余数是1. -但是,当除数是负数的时候,不同语言的运算规则会不一样。在C++/Java里面,整数的除法都是向零取整。比如说5/(-3),结果商是-1,余数是-2.这个余数因为是负数,是无法用来作为进制转换结果的。解决方案是:将商加上一,余数加上abs(K)。这样就转变成了向下取整的结果,余数也变成了正数。在这个例子中,结果商就是-1,余数是1. +但是,当除数是负数的时候,不同语言的运算规则会不一样。在C++/Java里面,整数的除法都是向零取整。比如说(-5)/(-3),结果商是1,余数是-2.这个余数因为是负数,是无法用来作为进制转换结果的。解决方案是:将商加上一,余数加上abs(K)。这样就转变成了向下取整的结果,余数也变成了正数。在这个例子中,结果商就是2,余数是1. 事实上,在wiki里面已经明确写明了negative base calculation的方法:https://en.wikipedia.org/wiki/Negative_base#Calculation -[Leetcode Link](https://leetcode.com/problems/convert-to-base--2) \ No newline at end of file +[Leetcode Link](https://leetcode.com/problems/convert-to-base--2) diff --git a/Readme.md b/Readme.md index ef22ace3b..ac05fb854 100644 --- a/Readme.md +++ b/Readme.md @@ -90,6 +90,7 @@ [1011.Capacity-To-Ship-Packages-Within-D-Days](https://github.com/wisdompeak/LeetCode/tree/master/Binary_Search/1011.Capacity-To-Ship-Packages-Within-D-Days) (M) [1060.Missing-Element-in-Sorted-Array](https://github.com/wisdompeak/LeetCode/blob/master/Binary_Search/1060.Missing-Element-in-Sorted-Array) (H) [1102.Path-With-Maximum-Minimum-Value](https://github.com/wisdompeak/LeetCode/tree/master/Binary_Search/1102.Path-With-Maximum-Minimum-Value) (H-) +[1631.Path-With-Minimum-Effort](https://github.com/wisdompeak/LeetCode/tree/master/Union_Find/1631.Path-With-Minimum-Effort) (H-) [1231.Divide-Chocolate](https://github.com/wisdompeak/LeetCode/tree/master/Binary_Search/1231.Divide-Chocolate) (M) [1283.Find-the-Smallest-Divisor-Given-a-Threshold](https://github.com/wisdompeak/LeetCode/tree/master/Binary_Search/1283.Find-the-Smallest-Divisor-Given-a-Threshold) (M) [1292.Maximum-Side-Length-of-a-Square-with-Sum-Less-than-or-Equal-to-Threshold](https://github.com/wisdompeak/LeetCode/tree/master/Binary_Search/1292.Maximum-Side-Length-of-a-Square-with-Sum-Less-than-or-Equal-to-Threshold) (H-) @@ -231,6 +232,7 @@ [1666.Change-the-Root-of-a-Binary-Tree](https://github.com/wisdompeak/LeetCode/tree/master/Tree/1666.Change-the-Root-of-a-Binary-Tree) (H-) [1932.Merge-BSTs-to-Create-Single-BST](https://github.com/wisdompeak/LeetCode/tree/master/Tree/1932.Merge-BSTs-to-Create-Single-BST) (H) [2003.Smallest-Missing-Genetic-Value-in-Each-Subtree](https://github.com/wisdompeak/LeetCode/tree/master/Tree/2003.Smallest-Missing-Genetic-Value-in-Each-Subtree) (H) +[2322.Minimum-Score-After-Removals-on-a-Tree](https://github.com/wisdompeak/LeetCode/tree/master/Tree/2322.Minimum-Score-After-Removals-on-a-Tree) (H-) * ``Path in a tree`` [543.Diameter-of-Binary-Tree](https://github.com/wisdompeak/LeetCode/tree/master/Tree/543.Diameter-of-Binary-Tree) (M) [124.Binary-Tree-Maximum-Path-Sum](https://github.com/wisdompeak/LeetCode/tree/master/Tree/124.Binary-Tree-Maximum-Path-Sum) (M) @@ -522,6 +524,7 @@ [499.The-Maze-III](https://github.com/wisdompeak/LeetCode/tree/master/BFS/499.The-Maze-III) (H) [787.Cheapest-Flights-Within-K-Stops](https://github.com/wisdompeak/LeetCode/tree/master/Graph/787.Cheapest-Flights-Within-K-Stops) (H) [882.Reachable-Nodes-In-Subdivided-Graph](https://github.com/wisdompeak/LeetCode/tree/master/BFS/882.Reachable-Nodes-In-Subdivided-Graph ) (H) +[1102.Path-With-Maximum-Minimum-Value](https://github.com/wisdompeak/LeetCode/tree/master/Binary_Search/1102.Path-With-Maximum-Minimum-Value) (H-) [1368.Minimum-Cost-to-Make-at-Least-One-Valid-Path-in-a-Grid](https://github.com/wisdompeak/LeetCode/tree/master/BFS/1368.Minimum-Cost-to-Make-at-Least-One-Valid-Path-in-a-Grid) (H) [1514.Path-with-Maximum-Probability](https://github.com/wisdompeak/LeetCode/tree/master/Graph/1514.Path-with-Maximum-Probability) (H) [1786.Number-of-Restricted-Paths-From-First-to-Last-Node](https://github.com/wisdompeak/LeetCode/tree/master/BFS/1786.Number-of-Restricted-Paths-From-First-to-Last-Node) (M+) @@ -622,6 +625,7 @@ * ``基本型 I`` [198.House-Robber](https://github.com/wisdompeak/LeetCode/tree/master/Dynamic_Programming/198.House-Robber) (E) [213.House-Robber-II](https://github.com/wisdompeak/LeetCode/tree/master/Dynamic_Programming/213.House-Robber-II) (M+) +[2320.Count-Number-of-Ways-to-Place-Houses](https://github.com/wisdompeak/LeetCode/tree/master/Dynamic_Programming/2320.Count-Number-of-Ways-to-Place-Houses) (M+) [1388.Pizza-With-3n-Slices](https://github.com/wisdompeak/LeetCode/tree/master/Dynamic_Programming/1388.Pizza-With-3n-Slices) (H-) [276.Paint-Fence](https://github.com/wisdompeak/LeetCode/tree/master/Dynamic_Programming/276.Paint-Fence) (H-) [265.Paint-House-II](https://github.com/wisdompeak/LeetCode/tree/master/Dynamic_Programming/265.Paint-House-II) (H) @@ -646,6 +650,7 @@ [1883.Minimum-Skips-to-Arrive-at-Meeting-On-Time](https://github.com/wisdompeak/LeetCode/tree/master/Dynamic_Programming/1883.Minimum-Skips-to-Arrive-at-Meeting-On-Time) (H) [2036.Maximum-Alternating-Subarray-Sum](https://github.com/wisdompeak/LeetCode/tree/master/Dynamic_Programming/2036.Maximum-Alternating-Subarray-Sum) (M+) [2143.Choose-Numbers-From-Two-Arrays-in-Range](https://github.com/wisdompeak/LeetCode/tree/master/Dynamic_Programming/2143.Choose-Numbers-From-Two-Arrays-in-Range) (H) +[2318.Number-of-Distinct-Roll-Sequences](https://github.com/wisdompeak/LeetCode/tree/master/Dynamic_Programming/2318.Number-of-Distinct-Roll-Sequences) (H-) * ``基本型 II`` [368.Largest-Divisible-Subset](https://github.com/wisdompeak/LeetCode/tree/master/Dynamic_Programming/368.Largest-Divisible-Subset) (M+) [300.Longest-Increasing-Subsequence](https://github.com/wisdompeak/LeetCode/tree/master/Greedy/300.Longest-Increasing-Subsequence) (M+) @@ -776,10 +781,11 @@ [1866.Number-of-Ways-to-Rearrange-Sticks-With-K-Sticks-Visible](https://github.com/wisdompeak/LeetCode/tree/master/Math/1866.Number-of-Ways-to-Rearrange-Sticks-With-K-Sticks-Visible) (H) * ``Infer future from current`` [2044.Count-Number-of-Maximum-Bitwise-OR-Subsets](https://github.com/wisdompeak/LeetCode/tree/master/Dynamic_Programming/2044.Count-Number-of-Maximum-Bitwise-OR-Subsets) (M) -* ``max subarray`` +* ``maximum subarray`` [053.Maximum-Subarray](https://github.com/wisdompeak/LeetCode/tree/master/Dynamic_Programming/053.Maximum-Subarray) (E+) [152.Maximum-Product-Subarray](https://github.com/wisdompeak/LeetCode/tree/master/Dynamic_Programming/152.Maximum-Product-Subarray) (M+) [2272.Substring-With-Largest-Variance](https://github.com/wisdompeak/LeetCode/tree/master/Dynamic_Programming/2272.Substring-With-Largest-Variance) (H-) +[2321.Maximum-Score-Of-Spliced-Array](https://github.com/wisdompeak/LeetCode/tree/master/Dynamic_Programming/2321.Maximum-Score-Of-Spliced-Array) (H-) #### [Bit Manipulation](https://github.com/wisdompeak/LeetCode/tree/master/Bit_Manipulation) [137.Single-Number-II](https://github.com/wisdompeak/LeetCode/tree/master/Bit_Manipulation/137.Single-Number-II) (H-) diff --git a/Tree/2322.Minimum-Score-After-Removals-on-a-Tree/2322.Minimum-Score-After-Removals-on-a-Tree.cpp b/Tree/2322.Minimum-Score-After-Removals-on-a-Tree/2322.Minimum-Score-After-Removals-on-a-Tree.cpp new file mode 100644 index 000000000..1c36e33b4 --- /dev/null +++ b/Tree/2322.Minimum-Score-After-Removals-on-a-Tree/2322.Minimum-Score-After-Removals-on-a-Tree.cpp @@ -0,0 +1,87 @@ +class Solution { + unordered_set next[1005]; // next[i]: a set of adjacent nodes next to i + int visited[1005]; + vectornums; + int n; +public: + int minimumScore(vector& nums, vector>& edges) + { + this->n = nums.size(); + this->nums = nums; + for (auto edge: edges) + { + int a = edge[0], b = edge[1]; + next[a].insert(b); + next[b].insert(a); + } + + int ret = INT_MAX; + for (auto edge: edges) + { + int a = edge[0], b = edge[1]; + next[a].erase(b); + next[b].erase(a); + + int ret1 = solve(a, b); + int ret2 = solve(b, a); + + ret = min(ret, min(ret1, ret2)); + + next[a].insert(b); + next[b].insert(a); + } + + return ret; + } + + int solve(int a, int b) + { + fill(visited, visited+n, 0); + visited[a] = 1; + int A = getAll(a); + + fill(visited, visited+n, 0); + visited[b] = 1; + int B = getAll(b); + + int ret = INT_MAX; + fill(visited, visited+n, 0); + visited[a] = 1; + dfs(a, ret, A, B); + return ret; + } + + int dfs(int node, int& ret, int A, int B) + { + int total = 0; + for (int nxt: next[node]) + { + if (visited[nxt]==1) continue; + visited[nxt] = 1; + + int C = dfs(nxt, ret, A, B); + int other = A^C; + int mx = max(other, max(C, B)); + int mn = min(other, min(C, B)); + ret = min(ret, mx-mn); + + total^=C; + } + total ^= nums[node]; + return total; + } + + int getAll(int node) + { + int total = 0; + for (int nxt: next[node]) + { + if (visited[nxt]==1) continue; + visited[nxt] = 1; + total ^= getAll(nxt); + } + total ^= nums[node]; + return total; + } + +}; diff --git a/Tree/2322.Minimum-Score-After-Removals-on-a-Tree/Readme.md b/Tree/2322.Minimum-Score-After-Removals-on-a-Tree/Readme.md new file mode 100644 index 000000000..8537558b9 --- /dev/null +++ b/Tree/2322.Minimum-Score-After-Removals-on-a-Tree/Readme.md @@ -0,0 +1,11 @@ +### 2322.Minimum-Score-After-Removals-on-a-Tree + +题目中说要删除两条边,同时考虑的话太复杂吃不消。我们不妨先枚举其中一条边(a-b),将其砍断的话整张图就变成了两棵树,分别以a和b作为根。此时我们还需要再砍一条边,但这条边只能存在于其中一棵树里面。不妨假设是砍在了a树。那么对于b树,我们就无脑取其所有节点的XOR(记做B)即可。 + +接下来看a树,需要将其砍一刀变成两个部分。此时我们发现,任意一刀,都会将a树里砍下一棵子树(假设称为c)来。那么我们需要计算的是c子树的节点XOR(记做C),和a子树剩下部分的XOR。突破口来了,剩下部分的XOR,其实就是a子树整体的XOR(记做A),与C做XOR即可。最终这两刀砍成的三个部分,就分别是B,C,和A^C。 + +此时我们看到,只要在a树里面DFS遍历节点,那么很容易用递归的方法,用o(n)的时间计算每个节点其下方所有孩子的XOR,也就是将其作为c子树的根时所对应的C值。如果我们提前计算了A和B,那么A^C也就马上有了。于是在DFS整棵a树的节点过程中,我们等同于遍历了第二刀的位置,同时立马得到了第二刀砍出的三个部分。 + +所以本题的思想是,暴力枚举第一刀,然后遍历其中的一颗子树枚举第二刀的位置。总的时间复杂度是o(N^2). + +当然,本题还有复杂度优化的空间,比如说用移根的方法来枚举第一刀,简化第二刀的遍历。这里不深究。 diff --git a/Trie/1268.Search-Suggestions-System/1268.Search-Suggestions-System_v1.cpp b/Trie/1268.Search-Suggestions-System/1268.Search-Suggestions-System_v1.cpp index d68782856..ec9dcebb9 100644 --- a/Trie/1268.Search-Suggestions-System/1268.Search-Suggestions-System_v1.cpp +++ b/Trie/1268.Search-Suggestions-System/1268.Search-Suggestions-System_v1.cpp @@ -1,26 +1,33 @@ -class Solution { - class TrieNode +class TrieNode +{ + public: + TrieNode* next[26]; + bool isEnd; + TrieNode() { - public: - TrieNode* next[26]; - int isEnd; - TrieNode() - { - for (int i=0; i<26; i++) - next[i]=NULL; - isEnd=0; - } - }; - TrieNode* root; - + for (int i=0; i<26; i++) + next[i]=NULL; + isEnd=0; + } +}; + +class Solution { + TrieNode* root; public: vector> suggestedProducts(vector& products, string searchWord) { root = new TrieNode(); - for (auto word: products) - insert(word); - - cout<next[ch-'a']==NULL) + node->next[ch-'a'] = new TrieNode(); + node = node->next[ch-'a']; + } + node->isEnd = true; + } vector>rets; TrieNode* node=root; @@ -38,50 +45,30 @@ class Solution { node = node->next[ch-'a']; word.push_back(ch); vectorans; - string temp = ""; - DFS(node,ans,temp); + string str = ""; + DFS(node,ans,str); - while (ans.size()>3) - ans.pop_back(); for (int j=0; jnext[ch-'a']==NULL) - node->next[ch-'a']=new TrieNode(); - node=node->next[ch-'a']; - } - node->isEnd+=1; - } + } - void DFS(TrieNode* node, vector&ans, string temp) - { - if (node->isEnd>0) - { - for (int k=0; kisEnd; k++) - ans.push_back(temp); - } - + void DFS(TrieNode* node, vector&ans, string& str) + { + if (ans.size()==3) return; + + if (node->isEnd == true) + ans.push_back(str); for (int i=0; i<26; i++) { - if (ans.size()>3) break; if (node->next[i]==NULL) continue; - temp.push_back('a'+i); - DFS(node->next[i],ans, temp); - temp.pop_back(); + str.push_back('a'+i); + DFS(node->next[i],ans, str); + str.pop_back(); } } - - }; diff --git a/Trie/1268.Search-Suggestions-System/Readme.md b/Trie/1268.Search-Suggestions-System/Readme.md index 46a03c266..540049610 100644 --- a/Trie/1268.Search-Suggestions-System/Readme.md +++ b/Trie/1268.Search-Suggestions-System/Readme.md @@ -1,14 +1,12 @@ ### 1268.Search-Suggestions-System #### 解法1: -比较严谨的做法是用Trie。根据searchWord的前缀(比如说前k个字母)在Trie中推进直至某个节点,然后以这个节点为根,用DFS的方法、根据先左后右的规则,找出最多三个合法的子路径(即能够拼成一个product)。 - -特别注意,此题中的product允许有重复,所以在TrieNode定义中,传统的bool isEnd需要改造为int count,并在构造整颗树的时候用它来计算有多少个重复(即共享完全相同的路径)的product。在DFS的过程中,如果推进到某个节点的count>1,说明会有多于1个product有着相同的名字,我们需要把它们都算上。 +比较严谨的做法是用Trie。先将所有的products的单词建树。然后,根据searchWord的前缀(比如说前k个字母)在Trie中推进直至某个节点node,然后以node为根进行DFS,根据先序遍历的规则(优先走左下方的子树),找出字典序最小的三个到叶子节点的子路径。 #### 解法2: 有一种非常取巧的方法,那就是将所有product按照字典排序。然后查找searchWord在里面的位置(用lower_bound定位),得到的就是字典序恰好大于等于searchWord的那个单词。我们查看以这个单词开始的连续三个单词,是否与searchWord共享指定书目的前缀,是的话就相应收入囊中。 -这种方法可以不必理会products中是否存在重复。但是第一步排序的过程其实比较耗时,不过题目给出了```1 <= Σ products[i].length <= 2 * 10^4```,这就是暗示了字符串排序的复杂度是可以接受的。 +这种方法第一步排序的过程其实比较耗时,不过题目给出了```1 <= Σ products[i].length <= 2 * 10^4```,这就是暗示了字符串排序的复杂度是可以接受的。 -[Leetcode Link](https://leetcode.com/problems/search-suggestions-system) \ No newline at end of file +[Leetcode Link](https://leetcode.com/problems/search-suggestions-system) diff --git a/Union_Find/1631.Path-With-Minimum-Effort/Readme.md b/Union_Find/1631.Path-With-Minimum-Effort/Readme.md index c9565cf93..f71a1fdd4 100644 --- a/Union_Find/1631.Path-With-Minimum-Effort/Readme.md +++ b/Union_Find/1631.Path-With-Minimum-Effort/Readme.md @@ -7,3 +7,5 @@ #### 解法2:Union Find 我们将所有的边按照diff从小到大排个序。优先选最小的边,看能联通哪些点;再选次小的边,看能联通哪些点。直至选中某条边之后,发现起点和终点恰好联通,那么这条边的diff就是答案。 + +注:此题和[1102](https://github.com/wisdompeak/LeetCode/tree/master/Binary_Search/1102.Path-With-Maximum-Minimum-Value)非常相似。

AltStyle によって変換されたページ (->オリジナル) /