题目
给定一个非负整数数组,a1, a2, …, an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。
返回可以使最终数组和为目标数 S 的所有添加符号的方法数。
示例:
输入:nums: [1, 1, 1, 1, 1], S: 3
输出:5
解释:-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一共有5种方法让最终目标和为3。
题解一(二叉树)
class Solution {
int count=0;
public int findTargetSumWays(int[] nums, int target) {
dfs(nums,target,0);
return count;
}
public void dfs(int[] nums,int target,int i){
//i是用来记录循环次数和i所在的位置的nums数组值的
if(i==nums.length&&target==0){
count++;
return;
}
if(i<nums.length){
dfs(nums,target-nums[i],i+1);//注意:循环调用只能用+1!不能用++,栈会溢出!原因不明?
dfs(nums,target+nums[i],i+1);
}
}
}
思路:
-
二叉树:
0 -1 +1 -1 +1 -1 +1 ....... ...... ...
题解二(dp)
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int sum=0;
for(int num:nums){
sum+=num;
}
if(target>sum||(sum+target)%2==1){
return 0;
}
int newTarget=(sum+target)/2;
int[] dp=new int[newTarget+1];
dp[0]=1;
for(int num:nums){
for(int j=newTarget;j>=num;j--){
dp[j]=dp[j]+dp[j-num];
}
}
return dp[newTarget];
}
}
思路:
-
复杂度分析
- 时间复杂度:O(n * target)O(n∗targe**t)
- 空间复杂度:O(target)O(targe**t)
-
思路
- 01背包问题是选或者不选,但本题是必须选,是选+还是选-。先将本问题转换为01背包问题。
- 假设所有符号为+的元素和为x,符号为-的元素和的绝对值是y。
我们想要的 S = 正数和 - 负数和 = x - y
而已知x与y的和是数组总和:x + y = sum
可以求出 x = (S + sum) / 2 = target
也就是我们要从nums数组里选出几个数,令其和为target
于是就转化成了求容量为target的01背包问题 =>要装满容量为target的背包,有几种方案 - 特例判断
如果S大于sum,不可能实现,返回0
如果x不是整数,也就是S + sum不是偶数,不可能实现,返回0 - dp[j]代表的意义:填满容量为j的背包,有dp[j]种方法。因为填满容量为0的背包有且只有一种方法,所以dp[0] = 1
- 状态转移:dp[j] = dp[j] + dp[j - num],
当前填满容量为j的包的方法数 = 之前填满容量为j的包的方法数 + 之前填满容量为j - num的包的方法数
也就是当前数num的加入,可以把之前和为j - num的方法数加入进来。 - 返回dp[-1],也就是dp[target]