题目:416. 分割等和子集
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
- 每个数组中的元素不会超过 100
- 数组的大小不会超过 200
示例 1:
输入: [1, 5, 11, 5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].
示例 2:
输入: [1, 2, 3, 5]
输出: false
解释: 数组不能分割成两个元素和相等的子集.
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/partition-equal-subset-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
基本思想1:dfs(超时)
- 计算数组中全部元素的和sum,和为奇数肯定不能分割,和为偶数需要判断数组中是否有若干个元素的和为sum/2
- 利用dfs判断是否有若干个元素的和为sum/2(从该题的数据量来看,肯定会超时)
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = 0;
for(auto n : nums){
sum += n;
}
if(sum % 2)
return false;
int target = sum / 2;
return dfs(nums, 0, 0, target);
}
bool dfs(vector<int>& nums, int pos, int cur, int target){
if(target == cur){
return true;
}
else if(cur > target){
return false;
}
bool res = false;
for(int i = pos; i < nums.size() && !res; ++i){
res = dfs(nums, i + 1, cur + nums[i], target);
}
return res;
}
};
基本思想2:动态规划
将求是否有求和等于全部元素的和的一半(target)的若干个元素转化为0-1背包问题
- 将target看作背包的容量,数组中的每一个元素看作物品
- 状态:每一个元素,小于等于target的所有可能的情况
- 选择:该元素选或者不选
- 结果dp[i][j]:表示前 i 个元素(包括第 i 个元素)中是否有若干个元素的和为 j
- 状态转移方程:dp[i][j] = dp[i - 1][ j ] || dp[i - 1][ j - nums[i] ]
- 特别考虑:容量为0时,结果为true (一个元素都不装);第0个元素时,只有当该元素等于当前容量时,结果才为true
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = 0;
for(auto n : nums){
sum += n;
}
if(sum % 2)
return false;
int target = sum / 2;
vector<vector<bool>> dp(nums.size(), vector<bool>(target + 1, false));
for(int i = 0; i < nums.size(); ++i){
dp[i][0] = true;//当容量为0时,结果为true, (不装该物品)
for(int j = 1; j <= target; ++j){
if(i == 0){//第0件物品,只有当该件物品等于j时,结果为true
if(nums[i] == j)
dp[i][j] = true;
}
else{
dp[i][j] = dp[i - 1][j];
if(j >= nums[i])
dp[i][j] = dp[i][j] || dp[i - 1][j - nums[i]];
}
}
}
return dp[nums.size() - 1][target];
}
};
降维
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = 0;
for(auto n : nums){
sum += n;
}
if(sum % 2)
return false;
int target = sum / 2;
vector<bool> dp(target + 1, false);
for(int i = 0; i < nums.size(); ++i){
dp[0] = true;//当容量为0时,结果为true, (不装该物品)
for(int j = target; j >= nums[i]; --j){//从后往前遍历
if(i == 0){//第0件物品,只有当该件物品等于j时,结果为true
if(nums[i] == j)
dp[j] = true;
}
else{
dp[j] = dp[j] || dp[j - nums[i]];
}
}
}
return dp[target];
}
};