// 更新:发现targer为负数时过不了用例了。
题目
https://leetcode-cn.com/problems/target-sum/
方法三:01背包问题
代码我还是不会
分析
可以将这组数看成两部分,P 和 N,其中 P 使用正号,N 使用负号。
target=sum(P)-sum(N)
sum(P)+sum(N)+sum(P)-sum(N)=sum(P)+sum(N)+target
2sum(P)=sum(P)+sum(N)+target
2sum(P)=sum()+target
sum(P)=(sum()+target)/2
所以,只要找到一个取正的子集,使他们的和为(sum()+target)/2,就证明存在解。
代码
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int sum=0;
for(int num:nums)
sum+=num;
if(sum<target||(sum+target)%2==1)
return 0;
target=Math.abs((sum+target)/2);
int[] dp=new int[target+1];
dp[0]=1;
for(int num:nums){
for(int i=target;i>=num;i--){
dp[i]+=dp[i-num];
}
}
return dp[target];
}
}
结果
方法一:动态规划
分析
参考:https://leetcode-cn.com/problems/target-sum/solution/mu-biao-he-dong-tai-gui-hua-xiang-xi-yi-bu-bu-fen-/
dp[i][j] 表示用数组中的前 i 个元素,组成和为 j 的方案数。
转移方程:dp[i][j] = dp[i - 1][j - nums[i]] + dp[i - 1][j + nums[i]]。
也可以写成递推的形式:
dp[i][j + nums[i]] += dp[i - 1][j]
dp[i][j - nums[i]] += dp[i - 1][j]
初始状态:
由于数组中所有数的和不超过 1000,那么 j 的最小值可以达到 -1000。在很多语言中,是不允许数组的下标为负数的,因此我们需要给 dp[i][j] 的第二维预先增加 1000
复杂度
时间复杂度:O(N∗sum),其中 N 是数组 nums 的长度。
空间复杂度:O(N∗sum)。
代码
public class Solution {
public int findTargetSumWays(int[] nums, int S) {
//初始数组的和不会超过1000,所以把总和都加上1000防止数组索引有负数
int[][] dp = new int[nums.length][2001];
dp[0][nums[0] + 1000] = 1;
dp[0][-nums[0] + 1000] += 1;//防止nums[0]=0的情况
//有i个数相加
for (int i = 1; i < nums.length; i++) {
//找所有sum的可能性
for (int sum = -1000; sum <= 1000; sum++) {
if (dp[i - 1][sum + 1000] > 0) {
dp[i][sum + nums[i] + 1000] += dp[i - 1][sum + 1000];
dp[i][sum - nums[i] + 1000] += dp[i - 1][sum + 1000];
}
}
}
return S > 1000 ? 0 : dp[nums.length - 1][S + 1000];
}
}
方法二:空间优化后的动态规划
分析
动态规划的状态转移方程中,dp[i][…] 只和 dp[i - 1][…] 有关,因此我们可以优化动态规划的空间复杂度,只需要使用两个一维数组即可。
代码
public class Solution {
public int findTargetSumWays(int[] nums, int S) {
int[]dp=new int[2001];
dp[nums[0]+1000]=1;
dp[-nums[0]+1000]+=1;
for(int i=1;i<nums.length;i++){
int[] next = new int[2001];
for(int sum=-1000;sum<=1000;sum++){
if(dp[sum+1000]>0){
next[sum+nums[i]+1000]+=dp[sum+1000];
next[sum-nums[i]+1000]+=dp[sum+1000];
}
}
dp=next;
}
return S>1000?0:dp[S+1000];
}
}
复杂度
时间复杂度:O(N∗sum),其中 N 是数组 nums 的长度。
空间复杂度:O(sum)。
方法四:递归
分析
对每一位加正号或符号
代码
class Solution {
int res=0;
int s;
int[] nums;
public int findTargetSumWays(int[] nums, int S) {
this.nums=nums;
this.s=S;
dfs(0,0);
return res;
}
public void dfs(int index,int curSum){
if(curSum==s&&index==nums.length){
res++;
return;
}
if(index==nums.length)
return;
curSum=curSum+nums[index];//加上当前位
dfs(index+1,curSum);
curSum=curSum-2*nums[index];
dfs(index+1,curSum);
}