Dynamic Programming Problems

Dp with prefix / suffix result array

D. Sorting By Multiplication

 

 

F - Shortcuts 

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]. 

D - Road to Millionaire

F - Knapsack for All Subsets

E - Sugoroku 4 (Why do we need to put the iteration over spin times as the outer loop ? )

 

D - Robot Arms 2

[LeetCode 1125] Smallest Sufficient Team

[LeetCode 1681] Minimum Incompatibility 

B. Marvolo Gaunt's Ring

 

E - Traveler

 

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. 

 

F - Max Sum Counting

 

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();
    }

 

 

 

D - We Like AGC

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;
                    }
                }
            }

 

 

D - We Love ABC

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 - AtCoder Express 2

 

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!                                      
E2. Escape The Maze (hard version) Tree Dp, Dfs                                        
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 Google                                    
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 Google                                    
664. Strange Printer     https://www.cnblogs.com/lz87/p/16069996.html Google                                    
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
                                       

posted @ 2021-09-01 12:28  Review->Improve  阅读(113)  评论(0)    收藏  举报