Dynamic Programming学习笔记 (9) - 目标和 (力扣# 494)

123 阅读1分钟

目标和题面如下

给定一个整数数组 nums 和一个整数 target 。向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数以构造一个表达式,

例如,nums = [2, 1] , 可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。

实例如下:

输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 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
+1 + 1 + 1 + 1 - 1 = 3

从题目的字面含义出发,我们使用一个二维数组作为DP数组,该数组的第一维代表数列数组的下标,第二维代表所有可能的目标和,其范围从-total到total, total是数列中所有元素之和。DP[m][n]中的数据就是使用前m个元素可以计算得到n的所有方法的数量,而所有这些方法都可以用于DP[m + 1][n + nums[m+1]]和DP[m + 1][n - nums[m+1]]之中。

使用以上的规律,我们可以根据数列数组的下标从小到大,依次推进,最后返回DP数组中的第一维为数列数组最后一个元素,第二维为target的元素的数值,如下图所示

009.png

Java代码如下

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int N = nums.length;

        int total = 0;
        for (int i = 0; i < N; i ++) {
            total += nums[i];
        }

        if (target > total || target < -total) {
            return 0;
        }

        int[][] dp = new int[N][2 * total + 1];

        dp[0][ nums[0] + total] = 1;
        //+0 and -0
        dp[0][-nums[0] + total] += 1;

        for (int i = 1; i < N;  i++) {
            for (int j = -total; j <= total; j ++) {
                if (dp[i - 1][j + total] > 0) {
                    int x = j + nums[i];
                    if (x <= total) {
                        dp[i][x + total] += dp[i - 1][j + total];
                    }

                    x = j - nums[i];
                    if (x >= -total) {
                        dp[i][x + total] += dp[i - 1][j + total];
                    }
                }
            }
        }

        return dp[N - 1][target + total];
    }
}

要注意的是上面代码中为了保证下标有效,DP数组的第二维将需要对应的目标和加上total值,使其范围从[-total, total]转换成[0, 2 * total]。