给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
- 每个数组中的元素不会超过 100
- 数组的大小不会超过 200
示例 1:
输入: [1, 5, 11, 5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].
示例 2:
输入: [1, 2, 3, 5]
输出: false
解释: 数组不能分割成两个元素和相等的子集.
解答
参考评论区,使用bitset
集合or
运算来记录所有累加组合情况:
class Solution {
public:
bool canPartition(vector<int>& nums) {
if(nums.size() < 2)
return false;
int sum = accumulate(nums.begin(), nums.end(), 0);
if(sum & 1)
return false;
bitset<20001> bits(1);
for(auto num : nums){
bits |= bits << num;
}
return bits[sum >> 1];
}
};
基本的动态规划,dp[i][j]
记录nums
的前i
个数(包含索引i
)中是否存在一种组合使得累加和等于j
。
class Solution {
public:
bool canPartition(vector<int>& nums) {
if(nums.size() < 2)
return false;
int sum = accumulate(nums.begin(), nums.end(), 0);
if(sum & 1)
return false;
sum = sum >> 1;
vector<vector<bool>> dp(nums.size(), vector<bool>(sum+1, false));
for(int i = 0; i < nums.size(); i++){
for(int j = 0; j <= sum; j++){
if(i == 0){
dp[i][j] = nums[i] == j;
}
else
dp[i][j] = dp[i-1][j] || (j >= nums[i] ? dp[i-1][j-nums[i]] : false);
}
}
return dp[nums.size()-1][sum];
}
};
实际上上述dp
初始化时应该将所有的dp[i][0]
都初始化为true
,进一步得到dp[i][nums[i]]=true
,这样才符合我们对dp
的定义,但实际上不影响最终的结果,所以省略。
由于dp
矩阵实际上只需要一行,所以简化空间复杂度如下:
class Solution {
public:
bool canPartition(vector<int>& nums) {
if(nums.size() < 2)
return false;
int sum = accumulate(nums.begin(), nums.end(), 0);
if(sum & 1)
return false;
sum = sum >> 1;
vector<bool> dp(sum+1, false);
for(int i = 0; i < nums.size(); i++){
// 必须从大到小更新
for(int j = sum; j >= nums[i]; j--){
if(i == 0){
dp[j] = nums[i] == j;
}
else
dp[j] = dp[j] || dp[j-nums[i]];
}
}
return dp[sum];
}
};