class Solution {
public:
bool canPartition(vector<int>& nums) {
// 动态规划解法,将nums求和,背包容量是一半,物品就是nums里的所有元素看能不能刚好凑出来
int sum = 0;
for (int num : nums)
sum += num;
if (sum % 2 != 0)
return false; // 如果没法分成两半,不可能分割
sum = sum / 2; // 和分一半,背包容量为一半
int n = nums.size();
vector<vector<bool>> dp(n+1, vector<bool>(sum+1, false));
// dp[i][j]:背包容量为j,前i个物品是否能够恰好装满,装满为true
for (int i = 0; i < n; i++)
dp[i][0] = true; // 相当于背包被装满了
for (int i = 1; i < n+1; i++) {
for (int j = 1; j < sum+1; j++) {
if (j - nums[i-1] < 0) // 此时背包容量为j,判断是否能装下第i个元素,nums数组中,第i个元素的下标是i-1,
dp[i][j] = dp[i-1][j]; // 如果装不下,继承前一个状态的结果,也就是不加入第i个元素
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 sum = 0;
for (int num : nums)
sum += num;
if (sum % 2 != 0)
return false;
int n = nums.size();
sum = sum / 2;
// 问题转化为,nums当中的数,能否正好装满sum容量的背包 0-1背包问题
vector<bool> dp(sum+1, false);
// dp[j]:容量为j时是否能刚好被填满
dp[0] = true; // 容量为0时更正好被填满
for (int i = 1; i <= n; i++) {
for (int j = sum; j >= 1; j--) {
if (j - nums[i-1] >= 0)
dp[j] = dp[j] || dp[j-nums[i-1]];
}
}
return dp[sum];
}
};
class Solution {
public:
int lastStoneWeightII(vector<int>& stones) {
// 动态规划 分成两堆重量相等的石头
int sum = 0;
for (int stone : stones)
sum += stone;
int target = sum / 2; // 向下取整,以下求能装满target的最大重量
int n = stones.size();
vector<int> dp(target+1, 0);
// dp[j]:容量为j能装下的最大重量
dp[0] = 0;
for (int i = 1; i <= n; i++) {
for (int j = target; j >= 1; j--) {
if (j - stones[i-1] >= 0)
dp[j] = max(dp[j], dp[j-stones[i-1]] + stones[i-1]);
}
}
return sum - dp[target] - dp[target];
}
};