(LeetCode 494)目标和 [DFS + 分类讨论]

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/STILLxjy/article/details/85029954

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。
注意:

数组的长度不会超过20,并且数组中的值全为正数(改为非负数)。
初始的数组的和不会超过1000。
保证返回的最终结果为32位整数。

分析:

1:题目中有一个小错误:题目中说数组中的数全为正数,但是测试样例数组中是存在0的,所以应该改为全书非负数。

对于存在0的情况,其实也是本题的亮点所在吧。

首先我们求出数组中所有数的总和为sum,(即所有数标记为 +

而我们想通过 - 来使得结果和为 S

所以我们需要将总和为 taregtsum = (sum - S) / 2 的数标记为 -
因此,问题就转化为 数组中所有数的子集的和为targetsum的子集总数

若 S > sum 或 S < -sum 或 (sum - S) 为奇数时 不存在解

对于0的处理:
由于0被标记为 +- 对结果没有影响,所以0是可以在2者之间任意选择的
因此我们只需先不考虑0的存在,计算出 数组中所有正数的子集的和为targetsum的子集总数ans
我们假设数组中0的个数num0
那么,最终的结果为 ans * pow(2,num0)

AC代码:

class Solution {
public:
    int ans = 0;
    
    void getans(vector<int>& nums,int d,int n, int cursum, int targetsum)
    {
        if(d >= n)
        {
            if(cursum == targetsum) ans++;
            return;
        }
        if(cursum > targetsum) return;
        if(cursum == targetsum)
        {
            ans += 1;
            return;
        }
        for(int i=d;i<n;i++)
        {
            if(nums[i] > 0)
            {
                getans(nums,i+1,n,cursum+nums[i],targetsum);
            }
        }
    }
    
    int findTargetSumWays(vector<int>& nums, int S) {
        int sum = 0;
        int n = nums.size();
        int num0 = 0;
        for(int i=0;i<nums.size();i++)
        {
            sum += nums[i];
            if(nums[i] == 0) num0++;
        }
        if(S < -sum || S > sum || (sum - S) % 2 == 1) return 0;
        int targetsum = (sum - S) / 2;
        ans = 0;
        getans(nums,0,n,0,targetsum);
        ans *= pow(2,num0);
        return ans;
    }
};

猜你喜欢

转载自blog.csdn.net/STILLxjy/article/details/85029954