给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
每个数组中的元素不会超过 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
——题目难度:中等
0-1 背包问题。描述:
给你一个可装载重量为
W
的背包和N
个物品,每个物品有重量和价值两个属性。其中第i
个物品的重量为wt[i]
,价值为val[i]
,现在让你用这个背包装物品,最多能装的价值是多少?
这是一个典型动态规划问题,这个题目中的物品不可以分割,要么装进包里,要么不装,不能说切成两块装一半。这就是 0-1 背包这个名词的来历。
解题代码
class Solution {
public:
bool canPartition(vector<int>& nums) {
int n = nums.size();
int sum = 0;
for(int num : nums) {
sum += num;
}
if (sum % 2 != 0) return false; //奇数不能完成目标
sum = sum / 2;
//dp[i][j] = x 表示,对于前 i 个物品,当前背包的容量为 j 时,若 x 为 true,则说明可以恰好将背包装满,若 x 为 false,则说明不能恰好将背包装满。
vector<vector<bool>> dp(n + 1, vector<bool>(sum + 1, 0));
for(int i = 0; i < n + 1; i++) {
dp[i][0] = 1;
}
for(int i = 1; i < n + 1; i++) {
for(int j = 1; j <= sum; j++) {
if (nums[i - 1] > j) {
dp[i][j] = dp[i - 1][j];
} else {
dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i - 1]];
}
}
}
return dp[n][sum];
}
};
滚动数组优化
class Solution {
public:
bool canPartition(vector<int>& nums) {
int n = nums.size();
int sum = 0;
for(int num : nums) {
sum += num;
}
if (sum % 2 != 0) return false;
sum /= 2;
vector<bool> dp(sum + 1, 0);
dp[0] = 1;
for(int i = 0; i < n; i++) {
for(int j = sum; j >= 1; j--) {
if (j >= nums[i])
dp[j] = dp[j] || dp[j - nums[i]];
}
}
return dp[sum];
}
};