Summary of Dynamic Planning Backpack Classes

Posted by shinichi_nguyen on Sat, 16 Oct 2021 18:13:47 +0200

1. Fiborachi

thinking

(1) dp[i] Fibolacci number i, I is the number I I

(2) Recursive formula dp[i]=dp[i-1]+dp[i+1]

3. Initialize the first two 0 and 1

(4) Traversal order is from front to back

Examples

The problem here is that the value is too large and requires redundancy of 1000000007.

class Solution {
    public int fib(int n) {
         if(n<=1) return n;
         int[] dp=new int[n+1];
         dp[0]=0;
         dp[1]=1;
         for(int i=2;i<=n;i++){
             dp[i]=(dp[i-1]+dp[i-2])%1000000007;
         }
         return dp[n];
    }
}

2. Climb the stairs

thinking

Does the third staircase mean one or two more steps on the second floor and two more steps on the first. It's equivalent to just adding one or two steps to their existing choices without modifying the number of methods. That is, the number of methods on the second layer + the number of methods on the first layer will give you the number of methods on the third layer. And so on. Then you can get it.

(1) The array subscript is the layer number and the value is the number of methods

②dp[i]=dp[i-1]+dp[i-2];

③dp[0]=1,dp[1]=1

(4) Traversing from the front to the back, you need to know the methods of the first two layers to get the methods of the third layer.

Examples of Layer 3

class Solution {
    public int climbStairs(int n) {
        if(n<2) return 1;
        int[] dp=new int[n+1];
        dp[0]=1;
        dp[1]=1;
        for(int i=2;i<=n;i++){
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n];
    }
}

3. Use minimum cost to climb stairs

thinking

(1) dp means the minimum amount of energy spent after climbing the stairs on the second floor

(2) The recursive formula must be found from the least exhaustive staircase of the first two, and then climb the staircase of this floor

(3) Initialization means that both layers 0 and 1 need to take a step first, that is, to finish with the appropriate physical effort.

(4) The traversal order must be from front to back, and the smallest strategy behind is to walk before you know it

There are actually two ideas here, the first one is to take the first step, which is equivalent to climbing a certain staircase. The second one is that the first two initializations of dp are 0 because the first step defaults to not walking, meaning that i will go to the second staircase but i will stay there before i finish climbing the second staircase. The first one is that i will finish climbing first and then see if i want to go up one or two floors.

Another example is that each staircase has to eat chocolate to be physically strong. The first is to choose the floor with the least chocolate because you have eaten it and can walk to it. Then go to that floor and finish eating chocolate before you go to see several layers. The second is not to eat, and see who can reach me with the least chocolate below. Then finish eating chocolate on that floorThen come up to this layer, and then I get the chocolate layer and make the selection again.

class Solution {
    public int minCostClimbingStairs(int[] cost) {
         int[] dp=new int[cost.length];
         dp[0]=cost[0];
         dp[1]=cost[1];
         
         for(int i=2;i<cost.length;i++){
             dp[i]=Math.min(dp[i-1],dp[i-2])+cost[i];
         }

         return Math.min(dp[dp.length-1],dp[dp.length-2]);
    }
}

4. Different paths

Deep search ideas

In fact, this idea can be seen as a binary tree, carrying out two directions i+1 or j+1. Just go beyond the boundary and return, or reach the target and result + 1 returns. Then you go i n two directions, which is equivalent to a binary tree, m+n-2+1, m+n-2, but there is also a node i n the original position so+1. The time complexity is 2^(m+n-1)-1.

class Solution {
    int res=0;

    public void traversal(int i,int j,int m,int n){
        if(i>m||j>n) return ;
        if(i==m&&j==n) res++;
        
        traversal(i+1,j,m,n);
        traversal(i,j+1,m,n);
    }

    
    public int uniquePaths(int m, int n) {
        if(m==1&&n==1) return 1;
        
       traversal(1,1,m,n);
       return res;

    }
}

Dynamic planning ideas

(1) dp means how many roads to go to I and j

(2) Recursive relationships are to go up one step I-1 or back one step j-1, that is, to the left, then the current dp'[i]'[j] must be reached by a step more than dp[i-1] [j] or dp[i] [j-1], that is, to know how many paths to dp[i-1] [j] and dp[i] [j-1], then dp[i] [j] is the sum of these paths.

(3) Initialization is directly i 0 and must only follow a straight line, or 0 j has only one path. That is 1

(4) Traversal direction from left to right, from top to bottom.

Time complexity is m*n, space takes up n

class Solution {
    public int uniquePaths(int m, int n) {
         int[][] dp=new int[m][n];
         
         for(int i=0;i<m;i++){
             dp[i][0]=1;
         }

         for(int i=0;i<n;i++){
             dp[0][i]=1;
         }

         for(int i=1;i<m;i++){
             for(int j=1;j<n;j++){
                 dp[i][j]=dp[i-1][j]+dp[i][j-1];
             }
         }
         return dp[m-1][n-1];
    }
}

5. Different paths 2

thinking

It's basically the same as the different paths, but the difference is initialization and judgment of the roadblock. The first is initialization, where if dp[i][0] encounters a roadblock, it's 0, and before the roadblock it's 1. The other side is judgment of the roadblock, where the current location of dp[i][j] has a roadblock indication that it can't be reached.

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
         int[][] dp=new int[obstacleGrid.length][obstacleGrid[0].length];
         int m=obstacleGrid.length;
         int n=obstacleGrid[0].length;
         for(int i=0;i<m&&obstacleGrid[i][0]==0;i++) dp[i][0]=1;
         for(int i=0;i<n&&obstacleGrid[0][i]==0;i++) dp[0][i]=1;
         
         for(int i=1;i<m;i++){
             for(int j=1;j<n;j++){
                 if(obstacleGrid[i][j]==1) continue;
                 dp[i][j]=dp[i-1][j]+dp[i][j-1];
             }
         }
         return dp[m-1][n-1];
    }
}

6. Integer Split

thinking

(1) dp is the maximum splitting product of i

(2) Max (dp[i], max((i-j) * j, dp[i-j] * j), which means that the three values are compared. Currently, two numbers, because J has been split before, the product of J and I-J is not considered.In fact, it is the maximum product of the split i-j, so multiplying J again is the product of the split i-j+j, which means the split of I. J can be selected, and then just select the largest one.

(3) Initial values dp[0] and dp[1] cannot be split, so they will not be processed. But dp[2] can be split into two 1's. Then the maximum product is 1. You can start here. Both dp[0] and dp[1] are basically filtered out.

(4) Traversal must be from left to r i ght.

class Solution {
    public int integerBreak(int n) {
        int[] dp=new int[n+1];
        dp[2]=1;
        for(int i=3;i<=n;i++){
            for(int j=1;j<i-1;j++){
                dp[i]=Math.max(dp[i],Math.max((i-j)*j,j*dp[i-j]));
            }
        }
        return dp[n];
    }
}

7. Different Binary Search Trees

thinking

No matter how the values change, the binary search tree is a fixed type, as long as the number is the same. For example, 2 nodes, but the final result will be 2 if the values are different. 3 nodes, but because their size order must be fixed, the type of binary search tree is similar no matter who the values are.

(1) dp[i] refers to the maximum number of binary search trees generated by 1-i nodes

(2) dp[i]=(dp[j-1]*dp[j-i]) is equivalent to who is the intermediate node, then the type of two subtrees is multiplied by the number of binary tree types whose current node is the root, and then all possible root nodes are traversed once.

(3) Initialization of dp[0] is also a case.dp[1]=1. Others can be calculated.

(4) Traverse from left to right because he depends on the number of binary tree types in front.

class Solution {
    public int numTrees(int n) {
    if(n<2) return 1;
       int[] dp=new int[n+1];
       dp[0]=1;
       dp[1]=1;
       dp[2]=2;
       for(int i=3;i<=n;i++){
           for(int j=1;j<=i;j++){
               dp[i]+=(dp[j-1]*dp[i-j]);
           }
       }
       return dp[n];
    }
}

8. Backpack Theory

  • Initializing this is actually the first item. If the backpack is heavy enough, add the value of the first item.
  • The definition of dp is actually a choice of 0-i items, up to j backpack weight, what is the maximum value that can be loaded.

9. Split Equal and Subset

thinking

(1) dp is defined as a selection of 0-i items, the value of j, if J can be reached then it is true

(2) Recursive formula is dp[i] [j]= dp[i] [j]||dp[i-1] [j-nums[i]] equivalent to whether or not nums[i] can reach j, or nums[i], then whether or not a subset of 0-i-1 items can reach j-nums[i].

(3) The initial value is the first item dp[0] [nums[i], which happens to be true. This means choosing whether to add this nums[0] in the first item, which is feasible when the value j is exactly equal to nums[0].

(4) Traversal is from left to right.

class Solution {
    public boolean canPartition(int[] nums) {
         int sum=0;
         for(int num:nums){
             sum+=num;
         }

         if(sum%2==1) return false;
         int len=nums.length;
         int target=sum/2;
        
        boolean[][] dp=new boolean[len][target+1];
        
        if(nums[0]<=target){
            dp[0][nums[0]]=true;
        }
        
        for(int i=1;i<len;i++){
            for(int j=0;j<=target;j++){
                dp[i][j]=dp[i-1][j];

                if(nums[i]==j){
                    dp[i][j]=true;
                    continue;
                }
                
                if(nums[i]<j){
                   dp[i][j]=dp[i][j]||dp[i-1][j-nums[i]];
                }
                
            }
        }
        return dp[len-1][target];
        
    }
}

Idea 2 is to simulate a 01 backpack. The capacity of the backpack equals sum/2, where the value is nums[i]. DP means to choose between 0-i items and add nums[i]Ability to make capacity exactly equal to value. Here DP is the choice among 0-i items, the largest subset of value. This actually limits value, value cannot be greater than J. Every time j subtracts a nums[i] it adds a nums[i]. But it can be determined that j is used to be greater than or equal to this nums[i]. Previous dp[j]Perhaps already the sum of the largest subset.

class Solution {
    public boolean canPartition(int[] nums) {
        int len=nums.length;
        int sum=0;
        for(int num:nums){
            sum+=num;
        }

        if(sum%2==1) return false;
        int target=sum/2;
        int[] dp=new int[target+1];
        
        for(int i=0;i<len;i++){
            for(int j=target;j>=nums[i];j--){
                dp[j]=Math.max(dp[j],dp[j-nums[i]]+nums[i]);
            }
        }
        return dp[target]==target;
    }
}
class Solution {
    public boolean canPartition(int[] nums) {
        int len=nums.length;
        int sum=0;
        for(int num:nums){
            sum+=num;
        }

        if(sum%2==1) return false;
        int target=sum/2;
        int[][] dp=new int[len][target+1];

        for(int i=0;i<=target;i++){
            if(i>=nums[0]){
                dp[0][i]=nums[0];
            }
            
        }
        
        for(int i=1;i<len;i++){
            for(int j=0;j<=target;j++){
                dp[i][j]=dp[i-1][j];
                if(j>nums[i]){
                   dp[i][j]=Math.max(dp[i][j],dp[i-1][j-nums[i]]+nums[i]);
                }
                
            }
        }
        return dp[len-1][target]==target;
    }
}

Summary: In fact, the difference between 01 and 01 backpacks is that they are right when capacity and value are equal. So the value and capacity here is equivalent to a subset and a comparison with sum/2. It is equivalent to a backpack that will not overflow value, so why can't the value overflow be because the value and weight are the same, and the weight of the larger value is also large and can't be.Add it to your backpack. The challenge is to understand that a backpack is not a limit that can be overweight, and finally can be added through the sum of the most valuable subsets. Another challenge is why the largest subset must be j? The reason is that you can't be overweight and that the relative value of items of the same weight is the same each time they are added, so the greatest value may only be the multiple packs.What's your value? The maximum weight limit for bags here is sum/2, and the limit for 01 bags is weight. That's exactly what it means.

01 The backpack is actually a question of whether to choose the first item or not. Then this place is either nums[i] selected or not, is there any way to add the first two items up to the weight of j, if it can, then it is true. If not, it is false.

class Solution {
    public boolean canPartition(int[] nums) {
        int sum=0;
        for(int num:nums){
            sum+=num;
        }
        
        if(sum%2==1) return false;
        int len=nums.length;
        int target=sum/2;
        boolean[][] dp=new boolean[len][target+1];
        
        if(nums[0]<=target){
            dp[0][nums[0]]=true;
        }

        for(int i=1;i<len;i++){
            for(int j=0;j<=target;j++){
                dp[i][j]=dp[i-1][j];
                if(nums[i]==j){
                   dp[i][j]=true;
                   continue;
                }

                if(j>=nums[i]){
                    dp[i][j]=dp[i-1][j]||dp[i-1][j-nums[i]];
                }
            }
        }

        return dp[len-1][target];
    }
}

10. Weight of the last stone 2

thinking

This place's five-part routine is just a routine for backpacks. But why can it be converted into a thought for backpacks? Think about the virtual stones of x-y each time, which are equivalent to just changing the plus and minus once. Two original stones have changed camps. Stone 3 minus the new virtual stones to dismantle their weight or actually switch the plus and minus sign. It's also equivalent to two heaps of stones.If you want the smallest stone, the closer the weight of two heaps of stones is, the better, then it must be close to sum/2. Then you can turn into a backpack idea and solve the difference between the two heaps.

(1) dp means capacity, value is the weight of stones, bigger is better. Pick in the array

(2) Recursive formula is the same as backpack handling.

class Solution {
    public int lastStoneWeightII(int[] stones) {
        int len=stones.length;
        int sum=0;
        for(int i:stones){
            sum+=i;
        }
        int target=sum/2;
        int[] dp=new int[target+1];
         
        for(int i=0;i<len;i++){
            for(int j=target;j>=stones[i];j--){
                dp[j]=Math.max(dp[j],dp[j-stones[i]]+stones[i]);
            }
        }

        return sum-dp[target]-dp[target];
    }
}

11. Goals and objectives

thinking

This question is a thought that needs to be turned into a 01 backpack. You can know that the additive number is deterministic, x-(sum-x)=target. This place additions are the sum of positive numbers, and sum-x is the set of minus numbers. The final calculation is that the additive is a fixed value. Since it is a fixed value and there are many possibilities to get it, sum directly in the array. The equivalent is that the back-containment is an addition, and the item is a number. Value is a pair.How many ways should this add up. Plan the five tracks dynamically.

(1) dp[j] where j refers to the amount of backspace, that is, the additive number, and the value is the number of methods.

(2) dp[j]=dp[j]+dp[j-nums[i]] is actually either to choose this item or not to select it, where the sum of methods is required, and the sum is required, and the choice is not. If not selected, it is the number of methods in the first i-1 that can add up J. If selected, it is the number of methods in the dp[j-nums[i]. If only nums[i] is added, the number of methods in the dp[j-nums[i] can get j, that is, only dp[j-nums[i]]The number of methods is fine.

(3) For initialization, it must be dp[0], that is, when there are 0 items, it must be a weight that can make up 0.

(4) With respect to traversal, you can go backwards and forwards because it prevents the modified values from being reused from going backwards, because each value is taken to the 0-i-1 range instead of 0-I. If you go backwards, the subsequent recursion is wrong.

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
          int sum=0;
          for(int num:nums){
              sum+=num;
          }
          
          if((sum+target)%2==1) return 0;

        if(sum<Math.abs(target)) return 0;
          
          //Additive number
          int x=(sum+target)/2;
          

          //Find the number of methods that can reach this addition
          int[] dp=new int[x+1];
          
          dp[0]=1;
          
          for(int i=0;i<nums.length;i++){
              for(int j=x;j>=nums[i];j--){
                  dp[j]=dp[j]+dp[j-nums[i]];
              }
          }
          return dp[x];
           
          
    }
}

dfs violent solution

01 A backpack is equivalent to a binary tree solution. The time complexity is 2^n power and the space complexity is 1, ignoring the recursive space complexity.

class Solution {
    int sum=0;
    public void dfs(int[] nums,int i,int cur,int target){
        if(i>=nums.length){
            if(cur==target){
                 sum++;
            }
           
            return;
        }
        
        dfs(nums,i+1,cur+nums[i],target);
        dfs(nums,i+1,cur-nums[i],target);
        
    }
    public int findTargetSumWays(int[] nums, int target) {
         dfs(nums,0,0,target);
         
         return sum;
    }
}

Memorized search for dfs

In fact, it is the maximum number of methods when cur number is reached for the first item recorded. Each time a recursion is entered, it is judged whether there is a record. If there is no record, then recursion does not exist, then recursion operation is not necessary. The reason for the return value here is that dfs recursively gets the value of the first item. The maximum number of methods that can be assembled is j, and it needs two.The sum of the maximum methods selected is the current maximum number of methods. The two choices are (+) nums[i]. Then record.

class Solution {
    // int sum=0;
    Map<String,Integer> memory=new HashMap<>();
    public int  dfs(int[] nums,int i,int cur,int target){
        String key=i+"_"+cur;
        if(memory.containsKey(key)) return memory.get(key);
        if(i>=nums.length){
            memory.put(key,cur==target?1:0);
            return memory.get(key);
        }
        
        int left=dfs(nums,i+1,cur+nums[i],target);
        int right=dfs(nums,i+1,cur-nums[i],target);
        memory.put(key,left+right);
        
        return memory.get(key);
        
    }
    public int findTargetSumWays(int[] nums, int target) {
         
         
         return dfs(nums,0,0,target);
    }
}

12.1 and zero

thinking

In fact, it can become a 01 backpack problem. Here it is equivalent to a two-dimensional 01 backpack. That is two capacities. The title means the maximum of m0 and N 1 in the last subset. In other words, two backpacks are the maximum of m and the maximum of n. Each string is actually weight, split into 0 and 1, and put into its own backpack if both can fit into that one.Then give you the meaning of one dollar.

(1) dp[i] [j] where I and j are backcontainments, the value is the maximum number of subsets

(2) Recursive relationships are those backpacks that pick the largest subset from the previous weight. For example, dp[i] [j]=max (dp[i] [j],dp[i-zero] [j-one]+1) is equivalent to dp[j]=max (dp[j], dp[j-x]+1)Such an operation is only two bags, which need to meet the conditions of two bags, so the traversal situation will be more natural. Equivalent to selecting one item (string) at a time, or selecting or not, if not, according to the maximum subset of the two bags when there is no such item, then go

dp[i-zero] [j-one] to see if the maximum number of subsets + 1 is more than the subset when it is not selected. Here i-zero, and j-one, are equivalent to if you want to select the current string, you must let the capacity of bit zero and one out, and then look at the maximum number of subsets of i-zero, J-one capacity backpacks.

(3) Initialization is all 0 because there is a 01 backpack problem where each item has a value of 1 and two weights.

(4) Traverse from back to front.

Expand

Essentially, it is a backpack, but the difference is that 0 is a weight and 1 is also a weight, and each time you add it, the backpack must have enough space to hold the items. Items have two weights, or two backpacks, which need two weights to fit in at the same time.

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        int[][] dp=new int[m+1][n+1];
        
        for(String str:strs){
            int one=0;
            int zero=0;
            for(char c:str.toCharArray()){
                if(c=='1') one++;
                else zero++;
            }

            for(int i=m;i>=zero;i--){
                for(int j=n;j>=one;j--){
                    dp[i][j]=Math.max(dp[i][j],dp[i-zero][j-one]+1);
                }
            }
        }
        return dp[m][n];
    }
}

Originally, the i-latitude was used to represent traversing objects, and j and K were both traversing weights. However, it is important to note that traversing starts at j=0 and k=0 because later processing such as dp[i-1] [j-x] [k-x]At this point, you may need the front data. The backpack with the first item that doesn't have enough space to hold the most items is taken from the previous backpack. If the items don't conform to the rules, they can't be added, for example, if one of them is overweight.

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        int len=strs.length;
        int[][][] dp=new int[len+1][m+1][n+1];
        
        for(int i=1;i<=len;i++){
            int[] count=getCount(strs[i-1]);
             for(int j=0;j<=m;j++){
                 for(int k=0;k<=n;k++){
                     dp[i][j][k]=dp[i-1][j][k];
                     if(j-count[0]>=0&&k-count[1]>=0){
                         dp[i][j][k]=Math.max(dp[i][j][k],dp[i-1][j-count[0]][k-count[1]]+1);
                     }
                     
                 }
             }
        }
        return dp[len][m][n];
    }

    public int[] getCount(String s){
        int[] count=new int[2];
        for(char c:s.toCharArray()){
            if(c=='1'){
                count[1]+=1;
            }else{
                count[0]+=1;
            }
        }
        return count;
    }
}
class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        int len=strs.length;
        int[][] dp=new int[m+1][n+1];
        
        for(int i=1;i<=len;i++){
            int[] count=getCount(strs[i-1]);
             for(int j=m;j>=count[0];j--){
                 for(int k=n;k>=count[1];k--){
                     dp[j][k]=Math.max(dp[j-count[0]][k-count[1]]+1,dp[j][k]);
                 }
             }
        }
        return dp[m][n];
    }

    public int[] getCount(String s){
        int[] count=new int[2];
        for(char c:s.toCharArray()){
            if(c=='1'){
                count[1]+=1;
            }else{
                count[0]+=1;
            }
        }
        return count;
    }
}

13.518.Change II

thinking

A full backpack used here. A full backpack is an item that can be selected countless times. If you want to reuse it in a DP array, you can traverse back and forth, and the results from previous processing can be reused. That is, if dp[1]=dpw[1-weight]+1 is a new maximum, then dp[2]=dp[2-weight]+1, weight, if 1, is equivalent to adding the same item twice in dp[2]. If 2-D comparison is used, then 0-I is used to compare 0-i-1. Comparing 0-I is usually done by adding the same item. Because now the same layer is traversed.

(1) dp[j] where j refers to coin, the value is the maximum number of methods to make J.

(2) Recursive dp[j]=dp[j]+dp[j-coins[i].

(3) Initialization is dp[0]=1 When the change is 1, as long as you don't add money is the method

(4) Traverse from there, because dp[j] may be reused after updating, which is equivalent to adding an item once.

Another solution is to traverse directly through the dfs.

class Solution {
    public int change(int amount, int[] coins) {
        int len=coins.length;
         int[] dp=new int[amount+1];
        
        dp[0]=1;
        
        for(int i=0;i<len;i++){
            for(int j=coins[i];j<=amount;j++){
                dp[j]=dp[j]+dp[j-coins[i]];
            }
        }
        return dp[amount];
    }
}

14. Combination sum and 4

thinking

This question is really a complete backpack problem. Elements can be used multiple times. But the difference is that if you want to arrange them in full order, you can't walk through the inside through the outside with the weight. It becomes the outside with the weight inside. In fact, its DP recursion relationship will not change. dp[j] is the total number of ways to reach the target J.[0]=1 as the initialization. Then the choice can be two first choices are not choosing this item, the second choices are choosing this item, but the two methods need to be added together to get the total method, because they are all different ways to reach the J capacity.

1 dp[j], J capacity backpack, value is the maximum method

(2) Recursive dp[j]=dp[j]+dp[j-nums[i]]

(3) The traversal direction is the outer weight layer and the inner layer of the item. The reason from left to right is that the item can be selected several times. It is equivalent to that after adding the item, the dp at the back can access the dp of the added item again. But the reverse order must be that the dp at the front has not been processed, that is, the result after the previous item has been selected is used to calculate this time.Result. There is no overlay of this item. And the benefit of outer weight and inner weight is that the same weight, the same items are selected, and different sorting can be added to the result.

And the objects in this place are out of order, so initialization with a two-dimensional array is very difficult.

The same is true for the explanation here, dp[i-nums[0]+dp[i-nums[1]+dp[i-nums[2]. Different numbers end in different order at the end. They are also reusable as they are traversed through.

class Solution {
    public int combinationSum4(int[] nums, int target) {
        int len=nums.length;
        
        int[] dp=new int[target+1];
        dp[0]=1;
        
        for(int i=1;i<=target;i++){
            for(int j=0;j<len;j++){
                if(i>=nums[j]){
                   dp[i]+=dp[i-nums[j]];
                }
              
            }
        }
        return dp[target];
    }
}

Two-dimensional thinking

Because different sorting of numbers produces different schemes, that is, the first dimension is the length of the combination rather than the number of items that the i-latitude already represents. If different sorting of numbers results in a target, for example, 2,5 and 5, 2, then the formula for the full backpack can be applied directly. So what is the length of the combination?For example, if 4 is made up, then the combination length can only be 111 of the four. If 3 is made up, then 121, 211, 112 is made up. If so, what should the corresponding recursive formula be like? In fact, dp[i] [j] is the length i, and the maximum number of compaction methods for target J should be dp[i-1] [j-num]All totals. Because num equals the weight of the item. To make a combination of lengths i, you must choose another item out of the i-1 lengths.The item j-num chooses is num. Then the target j-num plus num is j, that is to say, the total number of J methods can be obtained by adding the number of methods that make up j-num. The last reason to record the res is that each time the maximum number of methods for the current combination length is found, the question is how many methods to get the target, so all lengths need to be collected..

Subproblem is defined as problem(i,j)=sum(problem(i-1,j-numx))

Time Complexity is target^2 *n Spatial Complexity is target^2

class Solution {
    public int combinationSum4(int[] nums, int target) {
        int len=target;
         int[][] dp=new int[len+1][target+1];
         dp[0][0]=1;
         int res=0;
         for(int i=1;i<=len;i++){
             for(int j=0;j<=target;j++){
                 for(int num:nums){
                     if(j>=num) {
                        dp[i][j]+=dp[i-1][j-num];
                     }
                 }
             }
             res+=dp[i][target];
         }
         return  res;
    }
}

Ideas for optimizing thinking reduction

Dp[j] is actually the number of schemes when J is compacted. So what's different from two-dimensional one? Why doesn't this need re-accumulation? The reason is simple because DP definitions are actually different. The last one added the combined length latitude, but there is no such latitude, and the definition of DP is the maximum number of methods to compact J. dp[j] has actually experienced all dp[j-num]The answer is the number of methods added together, that is, it has tried nums in all order.

class Solution {
    public int combinationSum4(int[] nums, int target) {
          
          int[] dp=new int[target+1];
          dp[0]=1;
          
          for(int j=0;j<=target;j++){
              for(int num:nums){
                 if(j>=num) dp[j]+=dp[j-num];
              }
          }
          return dp[target];
    }
}

15. Climb the stairs (step up)

thinking

Full backpack and basically the same idea as combining 4. Here n is the target, and arrays are 1 and 2. That's equivalent to items and weights. So you just need to traverse weights before traversing items. Make items sortable. The limitation of two-dimensional arrays is that i limits the sorting of items, but it can be chosen through nums. But it's a lot more difficult.The benefit of dimension arrays is that they handle duplicate selections for permutation and full backpack problems.

class Solution {
    public int climbStairs(int n) {
        //Equivalent to an object with only two Traversals
        int[] dp=new int[n+1];
        int m=2;
        dp[0]=1;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(i>=j){
                   dp[i]+=dp[i-j];
                }
                
            }
        }
        return dp[n];
    }
}

16. About climbing stairs and exchange for change II

What makes them different is that climbing stairs is a permutation, but the exchange for change is a combination. In fact, the number of climbing stairs is 1,2, and the exchange for change is 1,2,5. Extending the number of climbing stairs is actually the exchange for change. So what makes them different is the problem of combination and arrangement, which is actually the definition of sub-problems.

If it is a money exchange problem, it can be seen as a two-dimensional array, one-dimensional is to determine which coins are used, two-dimensional is to compact the target I. It can also be turned into a one-dimensional array. One-dimensional essence here is to be similar to the definition of two-dimensional. Here the sub-problem dp[k] [i]=dp[k-1] [i]+dp[k-1] [i-nums[k]]That means there are two ways to get the first k to i. The first way is to get there without the k-th coin. The second way is to get there before the k-th coin is dp[k] [i-nums[k]. If two orders are changed, traversing the weight of the backpack first and then traversing the item, then the item is actually dp[i] [j]=sum(dp[i] [i-j])The J here is 1,2,5.This is the sum of the coins i-1, i-2, i-5 here. The sub-problem with the original combination is to use the first k coins to combine the last amount, but the latter is to achieve the j's goal in different order at the end by only compacting different coins. That is, the coins are in an unrestricted order.The first dimension is not required to control the order and position of coins.

But for climbing stairs, the elements of the array are sorted, so the key to sorting is to first traverse the capacity of the backpack to traverse the items so that different orders like 1,2 or 2,1 can appear.

Essentially different

Their sub-questions are different. Zero money is not sorted, but each time they climb the stairs several floors, the same different steps are different answers, even if they reach that height as well. For example, if 2,5 can reach 7 heights, then 5,2 can also. This is different for climbing the stairs. At that time, it was the same for scraping money.

The sub-problem of coin collection is actually problem (i,j)=problem(i-1,j)+problem(i-1,j-nums[i]);One time here is whether you choose to add the ith coin or not, which adds up the number of methods to get the JS without adding a coin. It is clear that the coins here are ordered, only one coin per layer can be used, and each coin layer can be used more than once.

But the subtle problem with climbing stairs is problem (i, j)=sum (i, j-nums[i]). It's quite different here. To get to the J floor, just walk more nums [i] from the j-nums[i] floorAll the methods on the floor add up, but this way there will be the same selection of items for 2,5 and 5,2, but there are different ways to go up the stairs. Converting to coins means seven dollars, but the order of coins is different each time I pay. I give two then five, and five then two are the same.

17. Exchange of change

thinking

(1) How many coins do dp[j] need to reach J at least

(2) dp[j]=min(dp[j],dp[j-coins[i]) The coin with the least J can reach j by either dp[i-1] [j] or dp[i-1] [j-coins[i]] indicating that just one coins[i] is missing

(3) Regarding initialization, dp[0] must be 0 here, and all others are maxInteger, guaranteeing that the smallest coin will be taken. And only the coin dp[coins[i]-coins[i]] will equal dp[0], which means that the final result must be coin exactly j. If not, then maxInteger will not be able to take it. Bottom-up solution.

This question is a complete backpack. It is also a question of finding the smallest coin. The traversal order starts with the items, because they are a combination, not an arrangement, that is, the order of the items is defined. A traversal method that will not be used after a coin has been used up.

class Solution {
    public int coinChange(int[] coins, int amount) {
         int[] dp=new int[amount+1];
         int len=coins.length;
         Arrays.fill(dp,Integer.MAX_VALUE);
         dp[0]=0;
         for(int i=0;i<len;i++){
             for(int j=coins[i];j<=amount;j++){
                 if(dp[j-coins[i]]!=Integer.MAX_VALUE){
                    dp[j]=Math.min(dp[j],dp[j-coins[i]]+1);
                 }
                 
             }
         }
         return dp[amount]==Integer.MAX_VALUE?-1:dp[amount];
    }
}

18. Complete square number

thinking

Full backpack, the sum of squares is the array, that is, the item. Then n is the corresponding backpack, which requires combination, as long as you get the final result. So you don't need to sort all the numbers.

class Solution {
    public int numSquares(int n) {
        if(n<=1) return n;
        int[] dp=new int[n+1];
        
        Arrays.fill(dp,Integer.MAX_VALUE);
        dp[0]=0;
          for(int i=1;i<=n/2;i++){
              for(int j=i*i;j<=n;j++){
                  dp[j]=Math.min(dp[j],dp[j-i*i]+1);
              }
          }
        return dp[n];
    }
}

19 Word Split

thinking

Full backpack. Actually the word is the item, but you just need to compare the order prescribed by the backpack and whether it's the item within that length. If it's not, you can't load it. If it's successful, you can load it. Here you can only traverse the amount of backpack before traversing the item, because traversing the item first causes the item to be in the order alreadyFixed, applepenapple doesn't fit when it traverses the apple first but finds the second one won't fit. But traversal capacity can try a combination of different items into it. This makes it possible to fill the backpack. But it's basically filling the backpack with items, but you don't know the order of the backpacks, but it's like specifying this location.What is that, what can only be put.

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
          int len=s.length();
          boolean[] dp=new boolean[len+1];
          dp[0]=true;
          for(int i=0;i<=len;i++){
              for(String word:wordDict){
                  int wordLen=word.length();
                  if(i>=wordLen&&wordDict.contains(s.substring(i-wordLen,i))&&dp[i-wordLen]){
                      dp[i]=dp[i-wordLen];
                  }
              }
          }

          return dp[len];
          
    }
}

Recursive Memory Search

thinking

start is used to cut, and each time it is cut out, see if it corresponds to a word in the word list, and if it does, continue cutting down.

class Solution {
    
    public boolean wordBreak(String s, List<String> wordDict) {
         int[] memory=new int[s.length()+1];
         return backtrack(s,0,wordDict,memory);
    }

    public boolean backtrack(String s,int start,List<String> wordDict,int[] memory){
        if(start>=s.length()){
            return true;
        }
        
        if(memory[start]!=0){
            return memory[start]==1?true:false;
        }

        for(int i=start;i<s.length();i++){
            String sub=s.substring(start,i+1);
            if(wordDict.contains(sub)&&backtrack(s,i+1,wordDict,memory)){
                memory[start]=1;
                return true;
            }
        }
        memory[start]=2;
        return false;
    }

}

BFS ideas

This idea is actually a hierarchical traversal, a hierarchical traversal of a multifork tree, then pruning through a memo. First traverse the position I of the truncated string, then traverse down if the truncated string is in a dictionary then i+1 can join the queue at this time, which means the next level of traversal. The preceding nodes have matched their own wordsString, so you can return correct at the end if I is greater than this string length. Because start-i is a word, and the nodes that can be added to the node rows and columns are matched with a word in front of each other. No mismatch can be added

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
          boolean[] visited=new boolean[s.length()+1];
          
          Queue<Integer> queue=new LinkedList<>();
          queue.offer(0);
          while(!queue.isEmpty()){
              int start=queue.poll();
              if(visited[start]) continue;
              else visited[start]=true;

              for(int i=start;i<s.length();i++){
                  String sub=s.substring(start,i+1);
                   if(wordDict.contains(sub)){
                        if(i+1<s.length()){
                            queue.offer(i+1);
                        }else{
                            return true;
                        }    
                   }
              }
          }
          return false;
    }
}



Topics: Algorithm Dynamic Programming