每日一道算法题(5)--子集背包问题

题目

    子集背包问题: 输入一个只包含正整数的非空数组nums,请你写一个算法,判断这个数组是否可以被分割成两个子集,使得两个子集的元素和相等。

思路分析

    这道题是0-1背包问题的进阶版,思路也与它差不多。我们依旧采用'动态规划',状态是物品的个数和装载的目标重量。选择就和0-1背包问题一样了,在最后一个物品不能放下时就为就选择不放入,在最后一个物品能放下时就可以选择放入或不放入了。

    最小子问题就是目标重量为零时和可选的物品个数为零时。

0-1背包问题解答的文章为下链接:

https://blog.csdn.net/Ostkakah/article/details/119453862?spm=1001.2014.3001.5501

代码展示

public boolean canPartition(int[] nums) {
		int sum = 0;
		for(int i = 0; i <= nums.length; i++) {
			sum += nums[i];
		}
		sum /= 2;
		boolean [][] dp = new boolean[nums.length + 1][sum + 1];
		for(int i = 0; i < nums.length + 1; i++)//初始化
			for(int j = 0; j < sum + 1; j++) {
				if(i == 0) {
					dp[i][j] = false;//当可选择的重物个数为零时结果值为false
				}
				if(j == 0) {
					dp[i][j] = true;//当目标重量为零时结果为true
				}
			}
		for(int i = 1; i < nums.length + 1; i++)
			for(int j = 1; j < sum + 1; j++) {
				if(j < nums[i - 1]) {//不够放下该重物时,其结果就为该重物之前的所以可选重物对应的Boolean值
					dp[i][j] = dp[i - 1][j];
				}else {
					dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i - 1]];//可以选择放也可以不放,两者都要判断
				}
			}
		return dp[nums.length][sum];
	}

    此代码的空间复杂度太高了,我们可以对其进行'状态压缩'。

    '状态压缩'后的代码为下:

public boolean canPartition1(int[] nums) {
		int sum  = 0;
		for(int i = 0; i <= nums.length; i++) {
			sum += nums[i];
		}
		sum /= 2;//计算出和的一半
		boolean[] dp = new boolean[sum + 1];
		for(int i= 0; i < sum + 1; i++)
			dp[i] = false;//开始时可选的重物为零个,所以都为false
		dp[0] = true;//唯独目标重量为0时为true
		for(int i = 1; i < nums.length + 1; i++)
			for(int j = sum; j >= 1; j--) {
				if(j >= nums[i - 1]) {
					dp[j] = dp[j] || dp[j - nums[i - 1]];
				}//当j<nums[i - 1]就等于本身
			}
		return dp[sum];
	}

其遍历方式为:从右向右左从上到下,由于是一维数组,为了知道'左上'的值我们必须从右向左,如果还是从左向右的话就会出现'左上'的值被覆盖。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值