LeetCode 494. 目标和

题目: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位整数。

解题思路(LeetCode评论转载):

原问题等同于: 找到nums一个正子集和一个负子集,使得总和等于target

我们假设P是正子集,N是负子集 例如: 假设nums = [1, 2, 3, 4, 5],target = 3,一个可能的解决方案是+1-2+3-4+5 = 3 这里正子集P = [1, 3, 5]和负子集N = [2, 4]

那么让我们看看如何将其转换为子集求和问题:

                  sum(P) - sum(N) = target
sum(P) + sum(N) + sum(P) - sum(N) = target + sum(P) + sum(N)
                       2 * sum(P) = target + sum(nums)

因此,原来的问题已转化为一个求子集的和问题: 找到nums的一个子集 P,使得sum(P) = (target + sum(nums)) / 2

请注意,上面的公式已经证明target + sum(nums)必须是偶数,否则输出为0

代码:

    int findTargetSumWays(vector<int>& nums, int S) {
        
        int sum = 0; int res = 0;
        int size = nums.size();
        
        //遍历数组求和
        for(int num: nums) sum += num;
        //如果数组为空或者数组和小于目标数或者和为奇数则直接返回
        if(nums.empty() || sum < S || (S+sum)%2) return 0;

        //求取中间值
        sum = (S+sum)/2;
        //初始化dp数组用于存储从 0 到目标值可由子序列组成的个数
        int dp[sum+1];
        memset(dp, 0, sizeof(dp));
        dp[0] = 1;
        
        //求子序列和为 sum 的子序列个数
        for(int num: nums)
            for(int i = sum; i >= num; i--)
                dp[i] += dp[i-num];
        
        return dp[sum];
    }

猜你喜欢

转载自blog.csdn.net/qq_40697071/article/details/86598058