leetcode练习-416. 分割等和子集(dp 01背包问题)

给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

注意:

每个数组中的元素不会超过 100
数组的大小不会超过 200
示例 1:

输入: [1, 5, 11, 5]

输出: true

解释: 数组可以分割成 [1, 5, 5] 和 [11].

示例 2:

输入: [1, 2, 3, 5]

输出: false

解释: 数组不能分割成两个元素和相等的子集.

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/partition-equal-subset-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我们的问题可以抽象成01背包的问题
背包的总容量为sum/2,判断是否能填满这个背包作为我们的目标

第一版二维数组
bp[i][j] i是nums中下标i,j是当前和(我们需要将当前和保持在j,能保持就是true否则false)。
我们的目标是判断在当前所在位置能不能保持j。
我们的状态转移方程为:bp[i][j]=bp[i-1][j]||dp[i-1][j-nums[i];
左边表示我们不要当前的元素如果当前已经满足j这个目标了,右边表示我们选择当前元素使其能达到j. j-nums[i]为真表示我再装下一个nums[i]的元素就能使它满足j。
当然在当前元素大于当前上线和时我们肯定不能要,因为要了
就不符合我们要满足当前元素和为j的任务了。
所以继承之前为dp[i][j]=dp[i-1][j];
还要注意初始化,就是当前j为0是肯定满足的。
初始化dp[0][0]=true;
还加上dp[0][nums[0]]=true;

class Solution {
    public boolean canPartition(int[] nums) {
    	if(nums.length==1)
    		return false;
        int sum=0;
        for(int i:nums) sum+=i;	//先算出总和,总和为奇数就直接返回false
        if((sum&1)==1)				//判断奇数偶数
        	return false;
        //容量
        sum/=2;							//在数组中找到总和为sum/2的元素
        boolean dp[][]=new boolean[nums.length][sum+1];
        dp[0][0]=true;
        dp[0][nums[0]]=true;
        
        for(int i=1;i=nums[i])
	        		dp[i][j]=dp[i-1][j]||dp[i-1][j-nums[i]];
	        	else if(j==0)
	        		dp[i][j]=true;
	        	else
	        		dp[i][j]=dp[i-1][j];
        	}
        }
       return dp[dp.length-1][sum];
    }
}

第二版转化成一维数组
在之前的dp数组遍历赋值时,我们发现我们对于一个元素(除了第一个)都是只和左上方的区域有关系,我们可以把二维降到一维上。我们从外循环从底到高,内循环右往左(j sum-0)因为我们的当前元素只和之前包括当前有关,所以我们改变这一项的值不会对下一项(j-1)产生影响。不能从左到右,因为下一项和左边元素有关系,改变了就不能像在之前二维数组中的左上方一样的意义了(左边发生了改变在二维数组中就是左上方改变了,我们在二维数组中应该保证上一行是确定的,不是改变的)。

class Solution {
    public boolean canPartition(int[] nums) {
    	if(nums.length==1)
    		return false;
        int sum=0;
        for(int i:nums) sum+=i;
        if((sum&1)==1)
        	return false;
        //容量
        sum/=2;
        boolean dp[]=new boolean[sum+1];
        dp[0]=true;        
        for(int i=0;i=0;j--) {	
        		if(j>=nums[i])
        			dp[j]=dp[j]||dp[j-nums[i]];
        		//nums[i]超过当前项,我们不理他。
        	}
        }
       return dp[sum];
    }
}

01背包还有一种问题就是利润最大,上面的是能否装满。
i 是物体的下标,j是当前的最大体积
dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-w[i]]+v[i])
特殊(当前装不下)
dp[i][j]=dp[i-1][j]

二维也是和上面相似
dp[j]=Math.max(dp[j],dp[j-w[i]]+v[i]);

你可能感兴趣的:(算法,leetcode)