一、题目
二、题解
2.0_思路(写给自己看的)
1^这一个是错的
要求解表达式数目,则将dp数组含义先看为表达式值为target的不同数量
增加了一个新的数(遍历nums数组循环进入下一层)想要得到表达式值为target的组合种数
可以由target-这个数的组合数加上target+这个数的组合数
dp[target]=dp[target-nums[i]]+dp[target+nums[i]];
2^上面出现了重复计算
参考了代码随想录的方法
对于目标值target,得到target的式子可以写为
(a+b+c+…) - (x+y+z+…)=target
左部分(left)使用加号,右部分(right)减号
又因为left-right=sum;(sum已知)
所以原式可以写成:
left-(sum-left)=target
即:left=(target+sum)/2
最终问题就可以转为求nums数组中相加为( target+sum)/2的组合数
之前的问题是看能不能,这里是求组合数
所以要改变一下递推公式
2.1_动态规划
2.1.1_dp数组含义
一开始我的想法是对于target可以从(target-x)+和(target+x)-x两个方向计算target的组合数写完测试了一下,会出现重复计算的情况,而且边界情况不好处理。
后来参考了代码随想录的解法
问题转为求nums数组中和为(target+sum)/2的组合数
推导过程:
将能够组合成target的情况归纳成left(a+b+c+…)减去right(e+f+g+…)
又因为left+right=sum(nums数组的总和)
最后可以总结成left=(target+sum)/2
dp[i][j]含义:
nums数组中前i个数,和为j的组合数为dp[i] [j]
2.1.2_递推公式
之前的背包问题只要看装入的情况能不能满足条件
现在是求解组合数
所以递推公式改为:
// 注意是+=
dp[j]+=dp[j-num[i]]
2.1.3_初始化
第0层fori循环对应0个数字可以选择
当目标值为0(dp[0])时,不放入物品也是一种情况
所以dp[0]=1,而其他初始化为0
2.1.4_deBug&完整代码
deBug:
1^ nums={1},target=2
因为nums数组(全部为非负数)的总和小于target,无论怎么组合都无法变成target
// 当全部数(题目说明了全为非负数)相加还是小于target时,不存在方案
if (sum < target)
return 0;
2^ nums={7,9,3,8,0,2,4,8,3,9},target=0
当sum与target的奇偶性不同时,也是无论怎么组合都无法得到target
// 当sum和target奇偶性不同时(奇数加偶数结果为奇数,%2=1),不存在方案
if((sum+target)%2==1)
return 0;
3^ nums={100},target=-200
跟1^相同,也是target过大的情况
对1^优化一下:
// 当全部数(题目说明了全为非负数)相加还是小于target的绝对值时,不存在方案
if (sum < abs(target))
return 0;
完整代码:
int findTargetSumWays(vector<int>& nums, int target) {
int sum = accumulate(nums.begin(), nums.end(), 0);
// 当全部数(题目说明了全为非负数)相加还是小于target时,不存在方案
if (sum < target)
return 0;
// 当sum和target奇偶性不同时(奇数加偶数结果为奇数,%2=1),不存在方案
if ((sum + target) % 2 == 1)
return 0;
int newT = (sum + target) / 2;
vector<int> dp(newT+1, 0);
// 背包容量为0时,不放入数也是一种方案
dp[0] = 1;
// 递推公式遍历dp数组
for (int num_i : nums)
for (int j = newT; j >= num_i; j--)
dp[j] += dp[j - num_i];
return dp[newT];
}
总结
总是找不到转成背包问题的思路,多练几题找找感觉