leetcode494 目标和(Medium)

题目来源:leetcode494 目标和

题目描述:

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

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

解题思路:

参考讨论区:题解
此问题可以转化为子集划分问题

将一个集合划分成两个子集A,B, 问满足sum(A) - sum(B) = Target的分法有多少种

两端同时加上集合元素和sum,得到2 * sum(A) = Target + sum ==> sum(A) = (Target + sum) / 2

已知sum(A) = sum(B) + Target >= Target, sum = sum(A) + sum(B) >= sum(A)

故sum >= Target, 且(Target + sum) % 2 == 0

接下来就是一个求子序和为某值的子集数了, 也就是经典的01背包问题

class Solution {
public:
    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!=0){
            return 0;
        }
        else return subSets(nums,(sum+S)/2);
    }
    int subSets(vector<int> &nums,int sum){
        int n=nums.size(),i,j;
        vector<vector<int>> dp(n+1,vector<int>(sum+1,0));
        for(auto& v : dp) v[0] = 1;
        for(i=1;i<n+1;i++){
            int val=nums[i-1];
            for(j=0;j<=sum;j++){
                if(j>=val){
                    dp[i][j]=dp[i-1][j]+dp[i-1][j-val];
                }
                else dp[i][j]=dp[i-1][j];

            }
        }
        return dp[n][sum];
    }
};
posted @ 2020-09-11 17:42  拉里拉里啦啦  阅读(166)  评论(0编辑  收藏  举报