LeetCode494. Target Sum题解

1. 题目描述

You are given a list of non-negative integers, a1, a2, …, an, and a target, S. Now you have 2 symbols + and -. For each integer, you should choose one from + and - as its new symbol. Find out how many ways to assign symbols to make sum of integers equal to target S.
【翻译过来】:给了一个非负数的矩阵,里面的元素是[a0, a1, a2……]和一个目标值S,现在要在这些元素之间加入+或者-,使得里面的元素之间运算结果能够等于目标值S,求一共有多少种加入+或者-的方法。

2. 样例

Input: nums is [1, 1, 1, 1, 1], S is 3. 
Output: 5
Explanation: 

-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

There are 5 ways to assign symbols to make the sum of nums be target 3.

3. 分析

拿到题目之后,即可判断出是自己不熟悉的动态规划类问题。表面上来看是在数组元素中间插入+或者-使得数组元素运算等于目标值,实际上对这个问题进行变换,可以将问题简化。
【变换】:

  • 假设原数组为S,目标值为target,那么原数组必然可以分成两个部分,一个部分里面的元素前面需要加-,即运算的时候应该是做减法,另一个部分里面的元素前面需要加+,即运算的时候应该是做加法;
  • 我们将做加法部分的数组记为P,做减法部分的数组记为N,举个例子,例如S = {1,2,3,4,5},target = 3,那么有一种可以是1-2+3-4+5,即P = {1,3,5},N = {2,4};
  • 于是我们可以知道:target = sum(P) - sum(N);
  • 那么sum(P) + sum(N) + sum(P) - sum(N) = sum(S) + target = 2sum(P);
  • 那么sum(P) = [target + sum(S)] / 2;

根据以上的推导,我们可以得到这样的结论:我们需要找到这样一个子序列P,使得子序列之和等于原序列之和与目标值的和的一半,我们需要找到这样子序列的数目。

显而易见,如果原序列之和与目标值的和为奇数,肯定不存在这样的子序列,如果原序列之和小于目标值,也肯定不存在(因为这种情况下,即使我们填的都是+,让所有元素相加都小于目标值,肯定不存在满足条件的解决方案)。

排除了以上的两种特殊情况之后,我们接下来研究的就是在原数组中找到之和符合条件的子序列个数,非常明显DP的方法。
LeetCode494. Target Sum题解_第1张图片

4. 源码

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int S) {
        int sum = 0;
        for (int i = 0; i < (int)nums.size(); i++) {
            sum += nums[i];
        }
        if (sum < S || (sum + S) % 2 != 0) {
            return 0;
        }
        int target = (S + sum) / 2;
        int dp[target + 1];
        for (int i = 0 ; i < target+1; i++) {
            dp[i] = 0;
        }
        dp[0] = 1;
        for (int i = 0; i < (int)nums.size(); i++) {
            for (int j = target; j >= nums[i]; j--) {
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[target];
    }
};

5. 心得

推导的方案是参照讨论区的大佬们的思路,的确很厉害。但是发现自己对于DP还是不是很擅长,即使找到了问题的简化方案,找到满足条件子序列的个数使用dp[]数组,仍然不能完全理解代码的思路。

你可能感兴趣的:(算法题解)