赞
踩
给定一个只包含正整数的非空数组,是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
参考自:https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/0-1-bei-bao-wen-ti-bian-ti-zhi-zi-ji-fen-ge-by-lab/
这个问题,初看与背包没有任何关系,为什么说它是背包问题呢?
首先回忆一下背包问题的大致描述是什么:
那么对于这个问题,我们可以先对集合求和,得出 sum,把问题转化为背包问题:
第一步要明确两点:【状态】和【选择】
第二步要明确 dp 数组的定义
第三步,根据【选择】,思考状态转移的逻辑
回想刚才的 dp数组含义,可以根据【选择】对 dp[i][j] 得到以下状态转移:
注意到 dp[i][j] 都是通过上一行 dp[i - 1][…] 转移过来的,之前的数据都不会再使用了。
所以我们可以进行状态压缩,将二维 dp 数组压缩为一维,节约空间复杂度。
状态压缩的代码和之前的解法思路完全相同,只在一行 dp数组上操作,i 每进行一轮迭代,dp[j] 其实就相当于 dp[i][j],所以只需要一维数组就够用了。(第 i 次循环结束后 dp[j] 表示的就是我们定义的状态 dp[i][j])
唯一需要注意的是 j 应该从后往前反向遍历,因为每个物品(或者说数字)只能用一次,以免之前的结果影响其他的结果。 (参考背包九讲)
时间复杂度: O(n*sum)
空间复杂度: O(sum)
参考自:https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/0-1-bei-bao-wen-ti-xiang-jie-zhen-dui-ben-ti-de-yo/
等价转换:是否可以从这个数组中挑选出一些正整数,使得这些数的和等于整个数组元素的和的一半。 前提条件是:数组的和一定得是偶数,即数组的和一定得被2整除,这一点是特判。
本题与 0 - 1 背包问题有一个很大的不同,即:
这一点区别,决定了在初始化的时候,所有的值应该初始化为 false。
作为“0 - 1 背包问题”,它的特点是:“每个数只能用一次”。思路是:物品一个一个选,容量也一点一点放大考虑(这一点是“动态规划”的思想,特别重要)。
如果在实际生活中,其实我们也是这样做的,一个一个尝试把候选物品放入“背包”,看什么时候能容纳的价值最大。
具体做法是:画一个 len 行,target + 1 列的表格。这里 len 是物品的个数,target 是背包的容量。 len 行 表示一个一个物品考虑,target + 1 多出来的那 1 列,表示背包容量从 0 开始,很多时候,我们需要考虑这个容量为 0 的数值。
dp[i][j] = dp[i - 1][j] or dp[i]][j - nums[i]]
复杂度分析
dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]]
;0 - 1 背包问题常规优化:“状态数组”从二维降到一维,减少空间复杂度。
** 代码**
复杂度分析:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。