LeetCode—494. 目标和(Target Sum)——分析及代码(Java)

一、题目

给定一个非负整数数组,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。

提示:

  • 数组非空,且长度不会超过 20 。
  • 初始的数组的和不会超过 1000 。
  • 保证返回的最终结果能被 32 位整数存下。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/target-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

二、分析及代码

1. 动态规划

(1)思路

设 dp[i][j] 表示数组中前 i 个元素组成和为 j 的方案数,则对第 i+1 个元素

dp[i + 1][j - nums[i + 1]] += dp[i][j]
dp[i + 1][j + nums[i + 1]] += dp[i][j]

最终,dp[n][S] 就是所求解。
由于初始的数组的和不会超过 1000 ,即数组正负和范围在 [-1000,1000] 范围内,可将下标 j 整体加 1000,用 [0,2000] 表示 [-1000,1000] 的元素和情况。

(2)代码

class Solution {
    
    
    public int findTargetSumWays(int[] nums, int S) {
    
    
        if (nums.length == 0 || S > 1000 || S < -1000) // S 可能超出 [-1000,1000]
            return 0;
        
        int [][] dp = new int[nums.length + 1][2001];//[-1000,1000] 共有 2001 个数
        dp[0][1000] += 1;

        for (int i = 1; i <= nums.length; i++) {
    
    
            for (int j = 0; j < 2001; j++) {
    
    
                if (dp[i - 1][j] != 0) {
    
    
                    dp[i][j + nums[i - 1]] += dp[i - 1][j];
                    dp[i][j - nums[i - 1]] += dp[i - 1][j];
                }
            }
        }

        return dp[nums.length][1000 + S];
    }
}

(3)结果

执行用时 :10 ms, 在所有 Java 提交中击败了 68.57% 的用户;
内存消耗 :38.2 MB, 在所有 Java 提交中击败了 37.91% 的用户。

2. 动态规划+节省空间

(1)思路

因为 dp[i+1] 只和 dp[i] 有关,用两个一维数组迭代即可实现。

(2)代码

class Solution {
    
    
    public int findTargetSumWays(int[] nums, int S) {
    
    
        if (nums.length == 0 || S > 1000 || S < -1000) // S 可能超出 [-1000,1000]
            return 0;
        
        int [] dp1 = new int[2001];//[-1000,1000] 共有 2001 个数
        int [] dp2 = new int[2001];
        dp1[1000] += 1;

        for (int i = 0; i < nums.length; i++) {
    
    
            if ( (i & 1) == 1) {
    
    
                for (int j = 0; j < 2001; j++) {
    
    
                    if (dp2[j] != 0) {
    
    
                        dp1[j + nums[i]] += dp2[j];
                        dp1[j - nums[i]] += dp2[j];
                        dp2[j] = 0;
                    }
                }
            } else {
    
    
                for (int j = 0; j < 2001; j++) {
    
    
                    if (dp1[j] != 0) {
    
    
                        dp2[j + nums[i]] += dp1[j];
                        dp2[j - nums[i]] += dp1[j];
                        dp1[j] = 0;
                    }
                }
            }
        }

        return (nums.length & 1) == 1 ? dp2[1000 + S] : dp1[1000 + S];
    }
}

(3)结果

执行用时 :6 ms, 在所有 Java 提交中击败了 76.04% 的用户;
内存消耗 :37.7 MB, 在所有 Java 提交中击败了 39.46% 的用户。

3. 动态规划+节省空间+预判

(1)思路

在开始动态规划前,可通过计算所给数组和与 S 的关系,提前排除不可能实现的情况。

(2)代码

class Solution {
    
    
    public int findTargetSumWays(int[] nums, int S) {
    
    
        if (nums.length == 0 || S > 1000 || S < -1000) // S 可能超出 [-1000,1000]
            return 0;
        int sum = 0;
        for (int num : nums)
            sum += num;
        if (S < -sum || S > sum || ((S + sum) & 1) == 1)
            return 0;
        
        int len = 2 * sum + 1;
        int [] dp1 = new int[len];
        int [] dp2 = new int[len];
        dp1[sum] += 1;

        for (int i = 0; i < nums.length; i++) {
    
    
            if ( (i & 1) == 1) {
    
    
                for (int j = 0; j < len; j++) {
    
    
                    if (dp2[j] != 0) {
    
    
                        dp1[j + nums[i]] += dp2[j];
                        dp1[j - nums[i]] += dp2[j];
                        dp2[j] = 0;
                    }
                }
            } else {
    
    
                for (int j = 0; j < len; j++) {
    
    
                    if (dp1[j] != 0) {
    
    
                        dp2[j + nums[i]] += dp1[j];
                        dp2[j - nums[i]] += dp1[j];
                        dp1[j] = 0;
                    }
                }
            }
        }

        return (nums.length & 1) == 1 ? dp2[sum + S] : dp1[sum + S];
    }
}

(3)结果

执行用时 :4 ms, 在所有 Java 提交中击败了 82.14% 的用户;
内存消耗 :38 MB, 在所有 Java 提交中击败了 38.88% 的用户。

三、其他

暂无。

猜你喜欢

转载自blog.csdn.net/zml66666/article/details/112795967