给定一个非负整数数组,a1, a2, …, an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。
返回可以使最终数组和为目标数 S 的所有添加符号的方法数。
示例 1:
输入: 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。
思路: 暴力递归和01背包(动态规划)
暴力递归
对于每一个数nums[i]
只有两种操作,要么加,要么减,当遍历完数组并且与目标和相等,即找到一个符合组合。
public int findTargetSumWays(int[] nums, int S) {
return find(nums,S,0,0);
}
public int find(int[] nums,int S,int sum,int index){
if(index==nums.length&&S==sum){
return 1;
}
if(index==nums.length)
return 0;
return (find(nums,S,sum+nums[index],index+1)+find(nums,S,sum-nums[index],index+1));
}
0-1背包:
难点在将原问题转换成01背包,将原问题转换为由正数和x
和负数和y
组成,x,y分别为和的绝对值
x+y=sum
x-y=target
==>x=(sum+target)/2
这样我们就找数组中满足和为x的组合,典型的0-1背包问题(这个数装还是不装,总容量为X)
我们使用动态规划解决这个问题,设dp[i][j]
表示前i
个数和为j
的次数。状态方程如下。
当前的数可以选时:
dp[i][j]=dp[i-1][j]+dp[i-1][j-nums[i]]]
当前的数不能选时:
dp[i][j]=dp[i-1][j]
public int findTargetSumWays(int[] nums, int S) {
int sum=0;
for(int i=0;i<nums.length;i++)
sum+=nums[i];
if((S+sum)%2==1||S>sum)return 0;
int target=(S+sum)/2;
int[][] dp=new int[nums.length+1][target+1];
for(int i=0;i<=nums.length;i++)
dp[i][0]=1;
for(int i=1;i<=nums.length;i++)
for(int j=0;j<=target;j++){
if(nums[i-1]<=j)dp[i][j]=dp[i-1][j]+dp[i-1][j-nums[i-1]];
else
dp[i][j]=dp[i-1][j];
}
return dp[nums.length][target];
}