LeetCode——目标和

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

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

A:
1.递归
这个题虽然是典型的0-1背包,但由于有负数加入,实际上用递归比较好。

    private int count;

    public int findTargetSumWays(int[] nums, int S) {
        count = 0;
        calculate(nums, S, 0);
        return count;
    }

    private void calculate(int[] nums, int S, int i) {
        if (i == nums.length) {
            if (S == 0) {
                count++;
            }
            return;
        }
        calculate(nums, S - nums[i], i + 1);
        calculate(nums, S + nums[i], i + 1);
    }

2.动态规划
状态转移方程:

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

解释:j+nums[i]和j-nums[i]表示对nums[i]可以执行加,还可以执行减,那么dp[i][j]的结果值就是加/减之后对应位置的和。

明确base case:(关键
例如,在示例nums = [1, 1, 1, 1, 1]中在非负整数数组的前提下,由于为是要选择+或者-,那么:

  • 组成的最大和就是对所有元素全选+。sum = 5
  • 组成的最小和的是对所有元素全选-。sum = -5

说明我们的目标和S一定是 -5<= S <=5的,其范围长度length = sum*2 + 1。这样就确定下状态了。那我们的dp表就是:

因为整个状态的范围长度length = sum*2 + 1,而j是从0开始的,故其初始化的位置应为sum。由于状态转移方程为dp[i][j] = dp[i-1][j+nums[i]] + dp[i-1][j-nums[i]].故dp表初始化应为:

//如果nums[0]==0的话,那么+0和-0都应该算作其操作,故if(nums[0] == 0) dp[0][sum] = 2;
if(nums[0] == 0) dp[0][sum] = 2;
else{
    dp[0][sum+nums[0]] = 1;
    dp[0][sum-nums[0]] = 1;
}

最后结果:

    public int findTargetSumWays(int[] nums, int S) {
        int sum = 0;
        for (int num : nums)
            sum += num;

        if (Math.abs(S) > Math.abs(sum))
            return 0;

        int len = nums.length;
        // - 0 +
        int t = sum * 2 + 1;
        int[][] dp = new int[len][t];
        //初始化
        if (nums[0] == 0) {
            dp[0][sum] = 2;//0为正负
        } else {
            dp[0][sum + nums[0]] = 1;
            dp[0][sum - nums[0]] = 1;
        }

        for (int i = 1; i < len; i++) {
            for (int j = 0; j < t; j++) {
                //注意边界
                int l = (j - nums[i]) >= 0 ? j - nums[i] : 0;
                int r = (j + nums[i]) < t ? j + nums[i] : 0;
                dp[i][j] = dp[i - 1][l] + dp[i - 1][r];
            }
        }
        return dp[len - 1][sum + S];
    }
原文地址:https://www.cnblogs.com/xym4869/p/13030487.html