494. Target Sum

LeetCode

  • 题目地址:https://leetcode.com/problems/target-sum/#/description
  • 问题描述:给定一个数组,和一个目标数,数组中的每个数可以取+也可以取-,问加起来是目标数的途径有多少种
  • 解题思路:

    1. 最直接的思路就是,对于数组的size=n,一共有2的n次方种组合,那么挨个试,如果求和等于目标数target,那么途径就加一,但是因为做了很多重复的运算,比如对于[1,2,3]来说,如果已经算了1+2,那么只需要再加3即可得到1+2+3,但是2的n次方种组合没有利用之前运算的结果,这种方法会超时
    2. 用深度优先搜索,消除很多重复运算。这种算法首先要做个变换,这里简单解释一下:设取正的数和为P,取负的数和为N,全取正总和为sum,目标数为s,则有

      • P - N = s
      • P + N = sum

      可以提前算出来,那么满足条件的时候,P=(sum+s)/ 2=target,即sum+s一定是偶数,所以如果sum < s,那么肯定不存在,同时如果sum+s不为偶数那么也不存在,都返回0。所以对于所有的数组,挨个加入正数P中,如果满足条件则途径加1。因为是dfs,注意状态还原和递归的参数。但是dfs用时约600ms

    3. dfs用了大量的递归,耗时很大。可以换一种方法计算。

      int subsetSum(vector<int>& nums, int s) {
      int dp[s + 1] = { 0 };
      dp[0] = 1;
      for (int n : nums)
      for (int i = s; i >= n; i--)
          dp[i] += dp[i - n];
      return dp[s];
      }

      这段代码的大概意思是,dp[i]表示到使得和为i有多少种途径,dp[0]=1,显然
      和为0的途径是都不选即1.然后每遇到一个数字n,就能使和i-n变成和i,所以
      dp[i]+=dp[i-n],此时dp[i]代表n之前包括n的数字,使得和为i的途径有多少种。
      因为如果先更新i小的,会影响后面的更新,所以先更新i大的,再更新小的。

  • 代码:

  • 思路1:
//超时
class Solution {
public:
    bool check(vector<int>& nums, int &S, int n) {
        int sum = 0;
        int nCopy = n;
        for (int index = 0; index < nums.size(); index++) {
            //cout << index << " " << n << endl;
            if (n % 2 == 1) 
                sum += nums[index];
            else
                sum -= nums[index];
            n /= 2;
        }
        //cout << nCopy << " : " << sum << endl;
        if (sum == S) return true;
        else return false;
    }
    int findTargetSumWays(vector<int>& nums, int S) {
        //1表示+,0表示-
        int res = 0;
        for (int i = 0; i < 1 << nums.size(); i++) {
            if (check(nums,S,i))
                res++;
        }
        return res;
    }
};
  • 思路2:
//600ms
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int S) {
        //1表示+,0表示-
        int res = 0;
        //sum of nums + S = 2 * sum of +
        int sum = 0;
        for (int i = 0; i < nums.size(); i++) {
            sum += nums[i];
        }
        if ((sum + S) % 2 == 1) return 0;
        int sumplus = (sum + S) / 2;
        //cout << sumplus << endl;
        //dfs
        int cur = 0;
        return dfs(nums,cur,sumplus,0);
        //return res;
    }
    int dfs(vector<int>& nums, int& cur, const int& target, int i) {
        if (i == nums.size()) {
            return cur == target ? 1 : 0;
        }
        //选第i个和不选第i个
        int sum = 0;
        sum += dfs(nums,cur,target,i+1);  //不选
        cur += nums[i];
        sum += dfs(nums,cur,target,i+1);  //选
        cur -= nums[i];  //还原
        return sum;
    }
};
  • 思路3:
//9ms
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int s) {
        int sum = accumulate(nums.begin(), nums.end(), 0);
        return sum < s || (s + sum) & 1 ? 0 : subsetSum(nums, (s + sum) >> 1); 
    }   

    int subsetSum(vector<int>& nums, int s) {
        int dp[s + 1] = { 0 };
        dp[0] = 1;
        for (int n : nums)
            for (int i = s; i >= n; i--)
                dp[i] += dp[i - n];
        return dp[s];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值