给你一个非负整数数组 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 <= 200 <= nums[i] <= 10000 <= sum(nums[i]) <= 1000-1000 <= target <= 1000
题解:
思路:dp
类01背包问题:
- 设pos为取+的数字和,neg为取-的数字和(均为正数),则target=pos-neg=pos-(sum-pos)=2*pos-sum
- 故pos=(sum+target)/2>=0且为常数,因此此问题等价于求有多少种方式用nums[i]凑成和为pos
- 进而该问题抽象为:用价值与体积均为nums[i]的物品,恰好凑满容量为pos的背包方案数
- 状态定义:dp[j]为恰好能凑满容量为j的背包方案数
- 状态转移:背包容量能或者不能装下nums[i]。 当不能装下nums[i]时,方案数直接继承之前的dp[j; 当能装下nums[i]时,总的方案数为不考虑nums[i]的方案数+有nums[i]参与新增的方案数; dp[j] += dp[j - nums[i]],dp[j - nums[i]]种方案与nums[i]共同凑成pos,即1*dp[j - nums[i]]
- 状态初始化:dp[0]=1,因为后面总会一直查找至j=0,如dp[3] += dp[3-3],空集是任意一条有效路径的起点,当属一条
- 遍历顺序:i正序,j倒序
- 返回形式:dp[pos]就是凑成pos总的方案数
时间复杂度:O(n^2)
空间复杂度:O(n)
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int len = nums.length;
int sum = 0;
for (int num : nums) sum += num;
// pos为小数||target绝对值比sum还大
if ((sum + target) % 2 == 1 || Math.abs(target) > sum) return 0;
int pos = (sum + target) / 2;
int[] dp = new int[pos + 1];
dp[0] = 1;
for (int num : nums) {
for (int j = pos; j >= num; j--) {
dp[j] += dp[j - num];
}
}
return dp[pos];
}
}