思路:
转化为01背包求方法数问题
读题要仔细:1、数组里每个数都要用上。2、数组里每个数只能用一次,但是可以是+可以是-
储备:
494. 目标和 - 力扣(LeetCode)看这个题解吧!这个题解才好懂一点。看完再去看代码随想录的代码。代码随想录讲的太绕了。
但她的题解里的target是基于她自己的代码的。所以我们还是改成自己舒服的方式。
以后也是。力扣的题解区里也有代码随想录的题解评价。多看评价挑题解
问题重点:
1、和前面两道又不一样了。
前两道是均分求最大,这道题是求相等。还要所有方案。
这次和之前遇到的背包问题不一样了,之前都是求容量为j的背包,最多能装多少。
本题则是装满有几种方法。其实这就是一个组合问题了。
2、看力扣的题解的思路更清晰
01背包问题是选或者不选,但本题是必须选,是选+还是选-。先将本问题转换为01背包问题。
假设所有符号为+的元素和为x,符号为-的元素和的绝对值是y。
这样相当于把符号消去了。正负号我们额外考虑。对于原本是负数的那些我们额外自己加符号减去来考虑。
我们想要的 target = 正数和 - 负数和 = x - y
而已知x与y的和是数组总和:x + y = sum可以求出正数和 x = (target + sum) / 2
也就是我们要从nums数组里选出几个数,令其和为x
这样就消掉负数了。原先数组里正负数都要考虑。现在只需要考虑正数。只从正数里挑。转化为01背包问题了
也就是我们要从nums数组里选出几个数,令其和为x
于是就转化成了求容量为x的01背包问题 ======>要装满容量为x的背包,有几种方案
特例判断
如果target大于sum,不可能实现,返回0
如果x不是整数,也就是target + sum不是偶数,不可能实现,返回0
正数的和一定是偶数?可能无法用直觉理解。这是推导出来的。
只能说正数和等于(target+sum)/2。所以要是偶数。
dp[j]代表的意义:填满容量为j的背包,有dp[j]种方法。
因为填满容量为0的背包有且只有一种方法,所以dp[0] = 1
意为让和为j,有dp[j]种方法。所以一开始从后往前遍历会有出现0的情况。
以前是求最大、划均分。现在是求组合的方法数
状态转移:dp[j] = dp[j] + dp[j - nums[i]],
当前填满容量为j的包的方法数 = 之前填满容量为j的包的方法数 + 之前填满容量为j - num的包的方法数
也就是当前数num的加入,可以把之前和为j - num的方法数加入进来。
返回dp[-1],也就是dp[target]
意为和为j的方法数=之前和为j的方法数+之前和为j-nums[i]的方法数(此时的dp[j-nums[i]]为上一层循环,后得到的dp[j-nums[i]]。一层层放入新的物品后,累加方法数。)
j-nums[i]意为从j的总和中腾出nums[i]的空间。
3、

最后:
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
int sum=0;
int n=nums.size();
for (int i=0;i<n;i++) sum+=nums[i];
int x=(sum+target)/2;//x为正数和
if ((sum+target)%2==1) return 0;//必须是偶数
if (abs(target)>sum) return 0;//目标和超过最大和,也不可能
//接下来就是装满容量为x的背包,
//原本是全都要装,只是哪个正哪个负。现在转化为挑哪些为正即可。
vector<int> dp(x+1,0);//只需要用到x
//dp[j]表示和为j的话有dp[j]种方法。
dp[0]=1;//和为0的话就不取数
for (int i=0;i<n;i++) {
for (int j=x;j>=nums[i];j--) {//环环相扣
dp[j]+=dp[j-nums[i]];//从上一轮中再腾出nums[i]的空间来累加
}
}
return dp[x];
}
};
1184

被折叠的 条评论
为什么被折叠?



