494.目标和
494.目标和
给你一个整数数组 nums 和一个整数 target 。
向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 :
例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
示例 1:
输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3
示例 2:
输入:nums = [1], target = 1
输出:1
提示:
1 <= nums.length <= 20
0 <= nums[i] <= 1000
0 <= sum(nums[i]) <= 1000
-1000 <= target <= 1000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/target-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解
题目可以转换为 -> 把数分为两堆,两堆和之差为目标和,求分堆的方法数。
大堆和指元素和大的一堆
sum大堆和 - target = sum总堆和 - sum大堆和
--> 2*sum大堆和 = sum总堆和 + target
--> sum大堆和 = (sum总堆和 + target)/2
注意点1:
如果sum总堆和 + target是奇数,那么直接返回0,因为元素都是整数,sum大堆和不能出现小数。
注意点2:
如果target比sum总堆和都还要大,那么结果肯定是不满足的
注意点3:
target可以为负数,也就是 sum总堆和 - sum大堆和 - sum大堆和 = target
--> sum大堆和 = (sum总堆和 - target)/2
综合上述两种情况,sum大堆和 = (sum总堆和 + |target|)/2。
可以转化为-> 背包的容量 (sum总堆和 + |target|)/2,物品的重量为nums[i],物品的价值nums[i]。
可以转化为 -> 该背包有几种装法,组合问题
1.确定dp数组以及下标的含义
dp[j] 表示填满背包容量j有dp[j]种方法。
2.确定递推公式
有哪些来源可以推出dp[j]
- 填满j-nums[i]的背包有dp[j-nums[i]]种方法,也就是如果当前nums[i]放入背包那么就有dp[j-nums[i]]种方法
- 之前填满容量为j背包的方法数
两种方法都可以选择,那么dp[j] = dp[j] + dp[j-nums[i]]
3.dp数组如何初始化
dp[0] = 1,有一种装法
4.确定遍历顺序
先遍历物品
再遍历背包容量
for(int i=0;i<len;i++){
for(int j=bagWeight;j>=nums[i];j--){
dp[j] = dp[j] + dp[j-nums[i]]
}
}
5.举例推导dp数组
输入:nums = [1,1,1,1,1], target = 3
代码
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int sum = 0;
for(int num : nums){
sum +=num;
}
target = Math.abs(target);
if(sum < target) return 0;
if((sum + target)%2 == 1) return 0;
target = (sum + target)/2;
int [] dp = new int [target+1];
dp[0]=1;
for(int i=0;i<nums.length;i++){
for(int j=target;j>=nums[i];j--){
dp[j] += dp[j-nums[i]];
}
}
return dp[target];
}
}