494. 目标和

题目

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

注意

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

思路

  • 有加减两种操作,数组中的数都为正,如果全部相加,令和为sum。如果全部相减,和为-sum。这个就是数组中元素加加减减的边界,-sum~sum。
  • j表示-sum到sum中的一个数,现在求前i个数有几种方式得到j。因为数组的索引不能为负数,但是j有可能为负数,所以用 d p [ j + s u m ] [ i ] dp[j+sum][i] dp[j+sum][i]表示前i个数加减操作得到j的方法数。
  • d p [ j + s u m ] [ i ] = d p [ j − n u m s [ i − 1 ] + s u m ] [ i − 1 ] + d p [ j + n u m s [ i − 1 ] + s u m ] [ i − 1 ] dp[j+sum][i]=dp[j-nums[i-1]+sum][i-1]+dp[j+nums[i-1]+sum][i-1] dp[j+sum][i]=dp[jnums[i1]+sum][i1]+dp[j+nums[i1]+sum][i1]。要注意 j − n u m s [ i − 1 ] j-nums[i-1] jnums[i1]不能小于-sum。 j + n u m s [ i − 1 ] j+nums[i-1] j+nums[i1]不能大于sum。

代码

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int S) {
        int sum = 0;

        for ( auto& n : nums )
            sum += n;

        if ( sum < abs( S ) ) return 0;
        
        int dp[sum*2+1][nums.size()+1];
        memset( dp, 0, sizeof(dp) );

        dp[sum][0] = 1;

        for ( int i = 1; i <= nums.size(); ++i ) {
            for ( int j = -sum; j <= sum; ++j ) {
                int sub = j - nums[i-1] + sum;
                int add = j + nums[i-1] + sum;

                if ( sub >= 0 ) 
                    dp[j+sum][i] += dp[sub][i-1];

                if ( add <= 2 * sum )
                    dp[j+sum][i] += dp[add][i-1];
            }
        }

        return dp[S+sum][nums.size()];
    }
};

你可能感兴趣的:(Leetcode,Hot,100)