【刷题1】LeetCode 494. 目标和 java题解

// 更新:发现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
2
sum(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);
    }

复杂度

在这里插入图片描述

结果

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值