494.目标和

题目描述

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

注意:

  1. 数组非空,且长度不会超过20。

  2. 初始的数组的和不会超过1000。

  3. 保证返回的最终结果能被32位整数存下。

 

思路

(1)动态规划。dp(i)(sum)记为数组前i个数,和为sum的方案总数,则状态方程可以记为:

dp[i][sum] = dp[i-1][sum-nums[i]] + dp[i-1][sum+nums[i]]。

这题要注意的是,S可为负数,初始数组的和不超过1000,则sum可为-1000的情况,因为数组下标这里不能为负数,所以在下标原来的基础上加1000,还要注意边界情况。

(2)直接DFS,但是这样时间复杂度比较高。

(3)转成0-1背包问题。参考leetcode中文社区

可以将这组数看成两部分,P 和 N,其中 P 使用正号,N 使用负号,有以下推导:

sum(P) - sum(N) = target

sum(P) + sum(N) + sum(P) - sum(N) = target + sum(P) + sum(N) 2 * sum(P) = target + sum(nums) // 这里表面右侧必定是个偶数

因此只要找到一个子集,令它们都取正号,并且和等于 (target + sum(nums))/2,就证明存在解。

令这个和为V,也就是背包容量为V时,nums[i]为第i个物品的体积,dp[V]表示在背包体积为V时,在背包中找出物品体积总和为V的方案个数。

 

代码1

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int S) {
        int n = nums.size();
        vector<vector<int>> dp(n, vector<int>(2001, 0));
        dp[0][nums[0] + 1000] += 1;
        dp[0][-nums[0] + 1000] += 1;
        for (int i = 1; i < n; i++) {
            for (int sum = -1000; sum <= 1000; sum++) {
                if (sum-nums[i]+1000 >= 0)
                    dp[i][sum+1000] = dp[i-1][sum-nums[i]+1000];
                if (sum+nums[i]+1000 <= 2000)
                    dp[i][sum+1000] += dp[i-1][sum+nums[i]+1000];
            }
        }
        return S > 1000 ? 0 : dp[n - 1][S + 1000];
    }
};

 

代码2

class Solution {
public:
    int res = 0;
    int findTargetSumWays(vector<int>& nums, int S) {
        int n = nums.size();
        dfs(nums, S, 0, 0);
        return res;
    }

    void dfs(vector<int>& nums, int S, int index, int sum){
        if (index == nums.size()) {
            if (sum == S) res++;
            return;
        }
        dfs(nums, S, index+1, sum+nums[index]);
        dfs(nums, S, index+1, sum-nums[index]);
    }
};

 

代码3

class Solution {
public: 
    int findTargetSumWays(vector<int>& nums, int S) {
        int sum = accumulate(nums.begin(), nums.end(), 0);
        int target = S + sum;
        if (S > sum || target % 2 == 1) return 0;
        target = target / 2;
        vector<int> dp(target+1);
        dp[0] = 1;
        for (auto cur : nums){
            for (int i = target; i >= cur; --i){
                dp[i] += dp[i-cur];
            }
        }
        return dp[target];
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值