题目描述
分析
出题人肯定是要尽量避免太直接的模版套用
像这题一样,挖了很多坑。
首先是题目很难让人第一时间联想到01背包
这道题换一种描述方法就是找到一个子集,使子集的元素值总和刚好等于原集合之和的一半。
也就是说是一个背包容量为sum/2的01背包问题
另外,化解为这样之后你又会发现和写的和前面的模板题又不一样,因为这道题的求解并不是装进物品的最大价值,而是使背包刚好装满。
这样的话dp数组的元素就不是物品价值,而是装进物品的重量
于是得到dp数组的长度为sum+1
而且有递推公式为:
dp[j] = max( dp[j], dp[j - weight[i]] + weight[i] )
得到代码如下:
class Solution {
public:
bool canPartition(vector<int>& nums) {
if(nums.size() == 1 ||(nums.size() == 2 && nums[0] != nums[1])){
return false;
}
int sum = accumulate(nums.begin(),nums.end(),0);
if(sum%2){
return false;
}
sum/=2;
//到这里就变成了一个背包容量为sum的01背包问题
vector<int> dp(sum+1,0);
for(int i = 0; i < nums.size(); i++){
for(int j = sum; j >= nums[i]; j--){
dp[j] = max(dp[j],dp[j-nums[i]]+nums[i]);
if(dp[j] == sum){
return true;
}
}
}
return false;
}
};
后记
不能死板地套用模版,而是要理解清楚动态规划的原理,把分析五步骤和数组的设置原理搞清楚