494. 目标和 - 01背包中"装满背包有几种方法"的问题
✅01背包中"装满背包有几种方法"的问题
这道题难就难在如何把题面转化为背包问题
💡由题知left + right = sum, left - right = target
,故有left = (sum + target) / 2
,则此时就转化为了装满容量为left
的背包有几种方法
当然这里要讨论一些非法情况
- 如果
sum + target < 0
,则非法 - 如果
(sum + target)
不是偶数,则非法(因为这样子凑不了left
)
dp数组的含义
dp[j]
是装满容量为j
的背包的方法数
递推公式
-
容量为
j
的背包可以怎么得来呢,可以是容量为j - nums[0]
的背包加了一个物品nums[0]
而得来,可以是容量为j - nums[1]
的背包加了一个物品nums[1]
而得来。故这里装满容量为j
的背包的方法数就是把[0, nums.size() - 1]
里面能够装下的物品(``j >= nums[i]`)的方法数都加起来 -
故由上,对于装满背包的方法数的通用公式(大概):
dp[j] += dp[j - nums[i]]
,因为是要算方法数,所以是+=
初始化
- 这里因为要递增上去,所以要得到答案,需使得
dp[0] = 1
,其实这个也很好理解,当背包容量为0时,装满它的方法只有一个:什么也不装!
遍历顺序
- 物品正序,背包容量倒序(因为01背包每个物品只放一次),老生常谈
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
int sum = 0;
//算总和
for (int i = 0; i < nums.size(); i++) {
sum += nums[i];
}
//判断非法条件
if ((sum + target) % 2 != 0 || (sum + target) / 2 < 0)return 0;
target = (sum + target) / 2;
vector<int>dp (1020, 0);
dp[0] = 1;
//经典01背包
for (int i = 0; i < nums.size(); i++) {
for (int j = target; j >= nums[i]; j--) {
dp[j] += dp[j - nums[i]];
}
}
return dp[target];
}
};