LeetCode 416 分割等和子集
这道题可以看成是01背包问题,总和为m,则问题可以改写为是否能选出一些元素,恰好填满容量为m / 2的背包。
状态定义:
dp[i][j]: 表示使用第0到第i个物品能否填满容量为j的背包;
状态转移方程:设第i个物品的体积为nums[i];
if nums[i] == j:
此时一定可以填满,dp[i][j] = true;
if nums[i] < j:
此时分装上i和不装上i两种情况:
dp[i][j] = dp[i - 1][j] (不装i的情况) || dp[i - 1][j - nums[i]](装上i的情况)
if nums[i] > j:
此时i装不上去:dp[i][j] = dp[i - 1][j];
因为计算dp[i][j]时只考虑了上一行左边以及正上方的元素,所以如果倒着填表,就可以把二维DP数组压缩成一维DP数组。
// 分割等和子集 经典NP完全问题
// 动态规划,类比背包问题
class Solution {
public:
bool canPartition(vector<int>& nums) {
int numsSum = 0;
int n = nums.size();
for (int &num : nums) {
numsSum += num;
}
if ((numsSum & 1) == 1) return false; // 奇数直接返回false;
int target = numsSum / 2;
vector<bool> dp(target + 1);
dp[0] = true;
if (nums[0] <= target) dp[nums[0]] = true; // 先填第一行表
for (int i = 1; i < n; ++i) {
for (int j = target; j >= 0; --j) {
if (nums[i] <= j) dp[j] = dp[j] || dp[j - nums[i]];
}
}
return dp[target];
}
};