分割等和子集
给你一个 只包含正整数 的 非空 数组 nums
。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
示例 1:
输入:nums = [1,5,11,5] 输出:true 解释:数组可以分割成 [1, 5, 5] 和 [11] 。
示例 2:
输入:nums = [1,2,3,5] 输出:false 解释:数组不能分割成两个元素和相等的子集。
提示:
1 <= nums.length <= 200
1 <= nums[i] <= 100
这道题可以用回溯法暴力搜索来做,但是会超时,所以选择用dp来做
这题的思路和背包问题的思路类似,可以把背包问题的解决方法搬过来
背包问题需要求的是最大容量,这道题如果我们求出最大和为sum/2,那么就证明可以将数组分为两个元素和相等的子集
动规五部曲分析如下:
1.确定dp数组以及下标的含义
这里存储一维的数据dp[i],如果将背包问题的思路套到这道题里,那么就是前i的容量里和的最大值
这里容量其实我们可以用价值来代替,即容量和价值相同
2.确定递推公式
01背包的递推公式为:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
本题,相当于背包里放入数值,那么物品i的重量是nums[i],其价值也是nums[i]。
所以递推公式:dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
3.dp数组如何初始化
如果题目给的价值都是正整数那么非0下标都初始化为0就可以了,如果题目给的价值有负数,那么非0下标就要初始化为负无穷。
这样才能让dp数组在递推的过程中取得最大的价值,而不是被初始值覆盖了。
4.确定遍历顺序
两层循环遍历,外层i代表容量,内层j代表价值
5.举例推导dp数组
代码如下
class Solution { public: bool canPartition(vector<int>& nums) { int n = nums.size(); vector<int> dp(10001,0); int sum = 0; for(int i = 0;i < n;i++) { sum += nums[i]; } if (sum % 2 == 1) { return false; } int target = sum / 2; for(int i = 0;i < n;i++) { for(int j = target;j >= nums[i];j--) { dp[j] = max(dp[j],dp[j - nums[i]] + nums[i]); } } if (dp[target] == target) { return true; } return false; } };