LeetCode416, segmentation, etc. and subsets (converted to 0-1 knapsack problem)

Title description

https://leetcode-cn.com/problems/partition-equal-subset-sum/
Insert picture description here

Solution (knapsack type dp problem)

The official problem solving chapter is:
Insert picture description here
Let’s do the problem honestly:
this is a backpack type dp problem.

  • Thinking about the current problem, what are the variables to be described, such as the number of items, the weight of the backpack, whether it can be full, that is what we need to do in the first step: to clarify two points, "status" and "choice" , which are different The problem with sub-sequences and strings, but to describe a sub-problem phenomenon, the corresponding subscripts such as dp[i][j] are no longer anything from s[i…j], but i items Choose what to pack inside, which can fill the backpack with capacity j or reach the maximum value.
  • So what we need to do in the second step is to clarify the definition of the dp array.
  • Finally, the third step is to think about the logic of state transition based on the "choice".
class Solution {
    
    
    public boolean canPartition(int[] nums) {
    
    
        if(nums==null ||nums.length==0||nums.length==1) return false;
        int sum = 0;
        for(int i=0;i<nums.length;i++){
    
    
            sum+=nums[i];
        }
        if(sum%2!=0){
    
    //总和不是偶数,则不能划分两个子集元素和
            return false;
        }
        sum = sum/2;///寻找一个子数组的和为sum即可
        
        //定义dp数组----转化为0-1背包问题,在给定的N个物品里面,背包的重量恰好为sum,我们要装物品,恰好能装满背包
        boolean [][] dp = new boolean[nums.length+1][sum+1];//dp[i][j]=x表示给定的前i个物品,背包重量为j,是否能够恰好装满的结果

        //边界:dp[0][...]的值为false,表示没有物品装
        //dp[...][0]值为false,表示要装的重量为0,不装就是true
        //-------------------------------默认初始化就是false
         for(int i=0;i<nums.length;i++){
    
    
             dp[i][0] = true;//
         }
         int n = nums.length;

        //递推 已经知道了dp[i][j],----num[i]表示物品i的重量
        for (int i = 1; i <= n; i++) {
    
    //由于要求dp[i][j],要用到dp[i-1][j]所以两个维度都是从低到高
        for (int j = 1; j <= sum; j++) {
    
    
            if (j - nums[i - 1] < 0) {
    
    //第i个物品的重量为nums[i-1]
               // 背包容量不足,不能装入第 i 个物品
                dp[i][j] = dp[i - 1][j]; //不能装入,则背包重量还是j
            } else {
    
    
                // 装入或不装入背包  装入,则重量为j,上一个的要装的重量也为j
                //不装入,则现在dp[i][j] = 去掉要装的nums[i-1]
                dp[i][j] = dp[i - 1][j] | dp[i - 1][j-nums[i-1]];
            }
        }
    }
        return dp[n][sum];//n个物品,背包容量为sum,是否能恰好装满
    }
}

Insert picture description here

From the code we wrote, we can know that the value of the recursion is related to several adjacent states, so we can use state compression to reduce the space complexity.

class Solution {
    
    
    public boolean canPartition(int[] nums) {
    
    
        if(nums==null ||nums.length==0||nums.length==1) return false;
        int sum = 0;
        for(int i=0;i<nums.length;i++){
    
    
            sum+=nums[i];
        }
        if(sum%2!=0){
    
    //总和不是偶数,则不能划分两个子集元素和
            return false;
        }
        sum = sum/2;///寻找一个子数组的和为sum即可
        boolean [] dp = new boolean[sum+1];//dp[i][j]=x表示给定的前i个物品,背包重量为j,是否能够恰好装满的结果

    
        dp[0]=true;
        int n = nums.length;
        for (int i = 0; i <n; i++) {
    
    //由于要求dp[i][j],要用到dp[i-1][j]所以两个维度都是从低到高
        for (int j = sum;j>=0 ; j--) {
    
    //因为使用了状态压缩,所以之前的dp[j]就是上一次的dp[i-1][j],现在的dp[j]就是dp[i][j];
        //j应该从后往前反向遍历,因为每个物品(或者说数字)只能用一次,以免之前的结果影响其他的结果。
            if (j - nums[i] >= 0) {
    
    //能装的前提下,装或者不装,不能装,则其值还是dp[j] = dp[j],所以这里直接省去
                dp[j] = dp[j] | dp[j-nums[i]];
            }
        }
    }
        return dp[sum];//n个物品,背包容量为sum,是否能恰好装满
    }
}

Insert picture description here

Guess you like

Origin blog.csdn.net/qq_44861675/article/details/114552557