Dynamic Programming Problems
Dp with prefix / suffix result array
Key takeaway:
- use the fact that we can only skip at most 28 checkpoints -> there will be at most 28 skipped checkpoints in between any adjacent visited checkpoints.
- Try to use dp[i] to compute dp[i + 1], this way is less bug prone than going backward: to compute dp[i], try to use dp[i - 1].
E - Sugoroku 4 (Why do we need to put the iteration over spin times as the outer loop ? )
[LeetCode 1125] Smallest Sufficient Team
[LeetCode 1681] Minimum Incompatibility
C - Large RPS Tournament: similar with sparse table, define dp[i][j] as the winning hand of range [i, i + 2^j - 1], i.e, starting from index i, length is 2^j. dp[i][j] = winner of dp[i][j - 1] and dp[i + 2^(j - 1)][j - 1]. The only difference here is that 2^j can be very large and go over n - 1. Because each player's hand is defined as a circular array, so we can just use (i + 2^(j - 1)) % n as the new starting index.
A variation of the classic knapsack problem.
A O(N * maxV) solution with O(maxV) memory usage. This approach is a bit harder to come up with.
static void solve(int testCnt) {
for (int testNumber = 0; testNumber < testCnt; testNumber++) {
int n = in.nextInt(), mod = 998244353;
int[][] s = new int[n][2];
for(int j = 0; j < 2; j++) {
for(int i = 0; i < n; i++) {
s[i][j] = in.nextInt();
}
}
Arrays.sort(s, Comparator.comparingInt(e->e[0]));
int ans = 0;
int max = 5000;
//dp[i]: the number of ways to achieve sum i from the B
int[] dp = new int[max + 1];
//there is 1 way to achieve sum of 0, not picking any numbers from B
dp[0] = 1;
for(int i = 0; i < n; i++) {
//if picking b[i], then any new sum is going to be >= b[i]
for(int j = max; j >= s[i][1]; j--) {
//j is the new sum after adding b[i], if it is <= than current max of A, which is a[i].
//the number of ways to sum j - b[i] is the same with the number of ways to sum j by simply adding b[i] to
//each of the dp[j - b[i]] ways.
if(j <= s[i][0]) {
//add the number of ways to achieve sum j after adding b[i] to the ans because j <= a[i]
ans = (ans + dp[j - s[i][1]]) % mod;
}
//update the number of ways to achieve sum j.
dp[j] = (dp[j] + dp[j - s[i][1]]) % mod;
}
}
out.println(ans);
}
out.close();
}
An easier to come up solution using O(N * maxV) time and memory is as follows.
1. Sort input array pairs in increasing A order. This won't impact the result because we only care about subset here. The reason that we sort input in this way is that after sorting, if we process input from left to right, the current A[i] is always the current max.
2. Define dp[i][j] as the number of ways to achieve sum j with the last picked number as b[i]. dp[0][0] = 1.
3. Define a prefix sum array prefixSum[sum] as the sum from dp[0][sum] to dp[k][sum], where k is the last processed index.
4. from i to 1 to n, take each b[i]. Consider all possible new sums in range [b[i], maxV], if a new sum <= a[i], then we need to add prefixSum[new sum - b[i]] to ans, representing considering all the number of ways to get sum new sum - b[i] ending at different i' with i' < i.
5. update dp[i][new sum] to be prefixSum[new sum - b[i]], this should be done regardless if a[i] >= the new sum or not.
6. after considering all possible new sum in range[b[i], maxV], update each prefixSum[sum] to include the current iteration's computation results for next iteration.
If we do not have the prefix sum used here, the solution will be O(N^2 * maxV), which will be TLE. Also we should insert a (0, 0) pair for ease of implementation. It also helps define the initial dp and prefix sum states. Refer to the following code's comments.
static void solve(int testCnt) {
for (int testNumber = 0; testNumber < testCnt; testNumber++) {
int n = in.nextInt(), mod = 998244353;
int[][] s = new int[n + 1][2];
for(int j = 0; j < 2; j++) {
for(int i = 1; i <= n; i++) {
s[i][j] = in.nextInt();
}
}
Arrays.sort(s, Comparator.comparingInt(e->e[0]));
int ans = 0;
int max = 5000;
//dp[i][j]: the number of ways to achieve sum j with the last picked number as b[i]
int[][] dp = new int[n + 1][max + 1];
//we have already inserted an extra {0, 0} pair into the input, so by picking the 0th pair,
//we pick 0 from a and 0 from b, yielding a valid way.
dp[0][0] = 1;
//prefix sum that represents dp[0][sum] + dp[1][sum] + .... + dp[k][sum], k is the index of the last processed pair
//for example, when computing the new ways by adding b[i] to sum and use a[i] as the new max, we will need to add
//up all dp[0][sum - b[i]], dp[1][sum - b[i]], ...... dp[i - 1][sum - b[i]].
//This sum represents all the unique ways of getting sum - b[i]. They are unique because the way we defined our dp.
int[] prefixSum = new int[max + 1];
//current processed index is 0, and there is only 1 way to get sum 0 from all previously processed index.
prefixSum[0] = 1;
for(int i = 1; i <= n; i++) {
for(int currSum = s[i][1]; currSum <= max; currSum++) {
int prevSum = currSum - s[i][1];
if(currSum <= s[i][0]) {
ans = (ans + prefixSum[prevSum]) % mod;
}
dp[i][currSum] = prefixSum[prevSum];
}
for(int sum = 0; sum <= max; sum++) {
prefixSum[sum] = (prefixSum[sum] + dp[i][sum]) % mod;
}
}
out.println(ans);
}
out.close();
}
dp state: dp[i][j][k][l]: the number of valid strings of length i with the last 3 characters being j, k, l. The character mapping is:
static char[] map = {'B', 'A', 'C', 'G', 'T'};
When i <= 2, we do not have enough characters to perform check, so we use 'B' to represent this not enough characters case. Each time we consider a new character at position i, we need to include the previous 3 possible characters to check if these 4 characters form a valid substring. The character at ith position does not have any impact on previous characters of positions <= j - 4.
When updating dp table, we need to make sure we are only using matching characters dp state of length i - 1. For these 4 characters, the last one, which is also the one that is being considered must be a valid character from ACGT. This is why when looping for l, its range is [1, 4], not [0, 4].
int[][][][] dp = new int[n + 1][5][5][5];
dp[0][0][0][0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= 4; j++) {
for(int k = 0; k <= 4; k++) {
for(int l = 1; l <= 4; l++) {
for(int m = 0; m <= 4; m++) {
char[] c = new char[]{map[m], map[j], map[k], map[l]};
if(check(c)) {
dp[i][j][k][l] = (dp[i][j][k][l] + dp[i - 1][m][j][k]) % mod;
}
}
}
}
}
}
int ans = 0;
for(int j = 1; j <= 4; j++) {
for(int k = 1; k <= 4; k++) {
for(int l = 1; l <= 4; l++) {
ans = (ans + dp[n][j][k][l]) % mod;
}
}
}
Main idea:
If we fix a B at position i, then the total number of ABC is prefix_a[i - 1] * suffix_c[i + 1].
prefix_a[i - 1]: the total number of A contributed by all possible strings from s[0, i - 1];
suffix_c[i + 1]: the total number of C contributed by all possible strings from s[i + 1, n - 1] ;
So we just need to compute prefix_a and suffix_c independently.
For prefix_a, we only care about A or ?,
if s[i] = A, then prefix_a[i] = prefix_a[i - 1] + 3^(? count in s[0, i - 1]).
if s[i] = ?, then prefix_a[i] = prefix_a[i - 1] * 3 + 3^(? count in s[0, i - 1]). Here we must multiply prefix_a[i - 1] by 3, representing the impact of this ? on previous contribution of A.
It is similiar for suffix_c.
E - Safety Journey How do you improve the runtime from O(K * N^2) to O((N + M) * K) so for N and M up to 5000, the solution will be fast enough?
D - Mixing Experiment: dp[j][k]: the min cost of having j chemical a and k chemical b. When looping over each package, we need to update dp table from big to small to avoid the same package being used more than once.
D - Coloring Dominoes: 1D dp is sufficient, no need to add another dimension to differentiate color.
F - +1-1x2: go backward; memoization
D - FT Robot: constraints are too big for 2D DP. Can you check x and y separately with O(N^2) dp?
D - No Need: knapsack-style dp, O(N^3), how do you optimize it such that the runtime is O(N^2 * logN) or O(N^2) ?
| B2. K for the Price of One (Hard Version) | Greedy, Dp | After sorting A, it is optimal to buy goods from the smaller prices. When buying the ith item, if there are already at least K - 1 goods purchased prior to this, it is optimal the get these K - 1 goods for free, and we only need to pay the first i - k goods. |
1. cost[i]: is the min money needed to buy the first i goods;
2. cost[i] = cost[i - 1] + a[i], when i < k; 3. cost[i] = cost[i - k] + a[i], when i >= k. 4. The answer is max i such that cost[i] <= P. |
|||||||||||||||||||
| A. Alternative Thinking | Constructive, Dp |
Dp solution is more straightforward to me. It uses a similiar idea with computing
max subarray sum using Dp. |
||||||||||||||||||||
| A. DZY Loves Sequences | Dp |
Since we can only change at most 1 number, let's try to change each number
and get the best out of all those changes. If we change a[i], then we need to find the longest increasing subarrays that end at a[i - 1] and a[i + 1]. If we can concate these 2 subarrays by changing a[i], do it. Otherwise we take the longer subarray's length + 1. How do we compute longest subarray length? Dp! |
||||||||||||||||||||
| D. Program | Dp, Prefix/Suffix Min/Max, Sparse Table | A very good problem of combing prefix min/max and dp idea! |
Read your own comments at https://codeforces.com/contest/1473/submission/137065544
|
|||||||||||||||||||
| E2. Escape The Maze (hard version) | Tree Dp, Dfs |
Refer to added comments: https://codeforces.com/contest/1611/submission/137283169
|
||||||||||||||||||||
| E - Chain Contestant | ||||||||||||||||||||||
| E - Safety Journey | ||||||||||||||||||||||
| B. Count Subrectangles | Dp, Math |
If a rectange's area is K, then its two unqiue side length must be L1 * L 2 == K.
So we can iterator over all K's divisors in sqrt(K) time. For a given L1 and L2, we just need to find out the count of consecutive 1s of length L1 in A and length of L2 in B. This can be done in O(N) time: For a consecutive 1s of length L, it will contribute L - X + 1 toward cnt[X], where cnt[X] is the number of consecutive 1s of length X. |
||||||||||||||||||||
| C. Xenia and Weights | Dp, Graph | Can be solved using DFS or Dp | ||||||||||||||||||||
| E. New Year Parties | ||||||||||||||||||||||
| D. Make The Fence Great Again | Constructive | For each fence the increase time <= 2. If we increase a fence by 2 times, then we would get 3 different numbers v, v + 1, v + 2. Each fence has at most 2 neighbors, so one of these 3 numbers must be different with both neighbors. |
https://www.cnblogs.com/lz87/p/15816194.html | |||||||||||||||||||
| E1. Median on Segments (Permutations Edition) |
Prefix Sum, Binary Search
|
https://www.cnblogs.com/lz87/p/15835663.html | ||||||||||||||||||||
| D. Make Them Equal | Math | |||||||||||||||||||||
| D. Slime | Constructive |
If you assign a + or - to each a[i], can you have all 2^n possible +/- permutations?
If you can not, which particular permutations you can not achieve? |
||||||||||||||||||||
| C. Python Indentation | Standard Dp problem, the key is to come up with a good dp state and its transition. | https://www.cnblogs.com/lz87/p/15866944.html | ||||||||||||||||||||
| D. Easy Problem | Struggled to understand and implement | |||||||||||||||||||||
| 1692. Count Ways to Distribute Candies | https://www.cnblogs.com/lz87/p/15934063.html | |||||||||||||||||||||
| 2088. Count Fertile Pyramids in a Land | ||||||||||||||||||||||
| https://www.cnblogs.com/lz87/p/16061704.html | ||||||||||||||||||||||
| 818. Race Car | https://www.cnblogs.com/lz87/p/16069717.html | |||||||||||||||||||||
| 664. Strange Printer | https://www.cnblogs.com/lz87/p/16069996.html | |||||||||||||||||||||
| C. Get an Even String | https://www.cnblogs.com/lz87/p/16084970.html | |||||||||||||||||||||
| E - Rook Path | ||||||||||||||||||||||
| Maximum Cost of Trip With K Highways | BitMask Dp | |||||||||||||||||||||
| Stone Game VIII |
Game Theory, MinMax
|
|||||||||||||||||||||
| Sum Of Special Evenly-Spaced Elements In Array | Prefix sum | |||||||||||||||||||||
| Counting Tilings | ||||||||||||||||||||||
| 790. Domino and Tromino Tiling | Dp | Similar with Counting Tilings | ||||||||||||||||||||
| 2031. Count Subarrays With More Ones Than Zeros | Dp, FenwickTree | |||||||||||||||||||||
| 1655. Distribute Repeating Integers |
BitMask Dp, O(3^N) Submask enumeration for all masks of N digits
|
|||||||||||||||||||||
| D - I Hate Non-integer Number | ||||||||||||||||||||||
| B - Increasing Prefix XOR | Bitwise | |||||||||||||||||||||
| 2312. Selling Pieces of Wood | ||||||||||||||||||||||
| 1994. The Number of Good Subsets | Bitmask | |||||||||||||||||||||
| 1986. Minimum Number of Work Sessions to Finish the Tasks | Bitmask, Knapsack | |||||||||||||||||||||
| 1977. Number of Ways to Separate Numbers |
Longest Common Substring
|
|||||||||||||||||||||
| 446. Arithmetic Slices II - Subsequence | Dp with hashmap | |||||||||||||||||||||
| E - All-you-can-eat | Knapsack | |||||||||||||||||||||
| F - Expensive Expense | https://www.cnblogs.com/lz87/p/16744210.html | |||||||||||||||||||||
| 2430. Maximum Deletions on a String |
Longest common substring
|

浙公网安备 33010602011771号