494. 目标和

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

注意:

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

思路:正数的总和为P, 负数的总和为N, P-N=sum, P+N=target. 因此P=(sum+target)/2. 问题转化为找子集的和为(sum+target)/2的子集总数。零一背包问题,动态规划解决。dp[i][j]表示前i个数的子集和等于j的子集的个数。请参考416. 分割等和子集

1、如果没选nums[i]:dp[i][j] = dp[i-1][j]

2、如果选了nums[i]:dp[i][j] = dp[i-1][j-nums[i]]。其中dp[i-1][j-nums[i]]表示前i-1个数能够成的子集和为j-nums[i]的子集的个数

因此,状态转移方程为dp[i][j]= dp[i-1][j] + dp[i-1][j-nums[i]]。

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int S) {
        int sum = 0;
        for (auto x : nums) sum += x;
        if (sum<S || (sum + S) % 2 == 1) return 0;
        sum = (sum + S) / 2;
        vector<int>dp(sum + 1);
        dp[0]=1;
        for (auto x:nums){
            for (int j = sum; j >= x; --j){
                dp[j] += dp[j - x];
            }
        }
        return dp[sum];
    }
};
发布了312 篇原创文章 · 获赞 32 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/Scarlett_Guan/article/details/99186948