给定一个非负整数数组,a1, a2, …, an, 和一个目标数,S。现在你有两个符号 +-。对于数组中的任意一个整数,你都可以从 +-中选择一个符号添加在前面。
返回可以使最终数组和为目标数 S 的所有添加符号的方法数。

示例:

  1. 输入:nums: [1, 1, 1, 1, 1], S: 3
  2. 输出:5
  3. 解释:
  4. -1+1+1+1+1 = 3
  5. +1-1+1+1+1 = 3
  6. +1+1-1+1+1 = 3
  7. +1+1+1-1+1 = 3
  8. +1+1+1+1-1 = 3
  9. 一共有5种方法让最终目标和为3


提示:

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

回溯算法

class Solution {
public:
    int res = 0;
    int findTargetSumWays(vector<int>& nums, int S) {
        if(nums.size() <= 0){
            return 0;
        }
        backtrack(nums, 0, S);
        return res;
    }
    void backtrack(vector<int>& nums, int i, int S){
        if(i == nums.size()){
            if(S == 0){
                res++;
            }
            return ;
        }
        S = S + nums[i];
        backtrack(nums, i+1, S);
        S = S - nums[i];

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

// 计算nums中有几个子集的和为target
    int subsets(vector<int>& nums, int target){
        vector<vector<int>> dp(nums.size() + 1, vector<int>(target+1, 0));
        // base case
        for(int i= 0;i<=nums.size();i++){
            dp[i][0] = 1;
        }
        for(int i = 1; i<=nums.size();i++){
            for(int j = 0; j <=target; j++){
                if(j >= nums[i-1]){
                    // 两种选择之和
                    dp[i][j] = dp[i-1][j] + dp[i - 1][j - nums[i-1]];
                }else{
                    //背包的空间不足
                    dp[i][j] = dp[i-1][j];
                }
            }
        }
        return dp[nums.size()][target];
    }

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

// 计算nums中有几个子集的和为target
    int subsets(vector<int>& nums, int target){
        vector<int> dp(target+1, 0);
        // base case
        dp[0] = 1;
        for(int i = 1; i<=nums.size();i++){
            for(int j = target; j >=0; j--){
                if(j >= nums[i-1]){
                    // 两种选择之和
                    dp[j] = dp[j] + dp[j - nums[i-1]];
                }else{
                    //背包的空间不足
                    dp[j] = dp[j];
                }
            }
        }
        return dp[target];
    }

};