494. Target Sum

You are given a list of non-negative integers, a1, a2, …, an, and a target, S. Now you have 2 symbols + and -. For each integer, you should choose one from + and - as its new symbol.

Find out how many ways to assign symbols to make sum of integers equal to target S.

Example 1:
Input: nums is [1, 1, 1, 1, 1], S is 3.
Output: 5
Explanation:

-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

There are 5 ways to assign symbols to make the sum of nums be target 3.
Note:
The length of the given array is positive and will not exceed 20.
The sum of elements in the given array will not exceed 1000.
Your output answer is guaranteed to be fitted in a 32-bit integer.

该题是一道meduim的题,但是刚看到题意,立马有了回溯遍历的,但是细想,不可能会这么简单吧,但还是写了一下回溯的代码如下:

void dfs_Target(vector<int>& nums, int target, int pos, int subsum,int &count){
    if (pos == nums.size()){
        if (subsum == target)count++;
        return;
    }
    dfs_Target(nums, target, pos + 1, subsum + nums[pos], count);
    dfs_Target(nums, target, pos + 1, subsum - nums[pos], count);
}

int findTargetSumWays(vector<int>& nums, int S) {
    if (nums.size() == 0)return 0;
    int count = 0;
    dfs_Target(nums, S, 0, 0, count);
    return count;
}

私以为这样的时间复杂度很高,测试一下虽然过了,但是只超过16.7%的算法

细想有没有其他的算法能够解决此问题,冥思苦想一会不得其解,故上网上了看了一下别人的代码,果然有妙计,还要什么电视机。。。

int findTargetSumWays(vector<int>& nums, int S) {
    if (nums.size() == 0)return 0;
    unordered_map<int, int> dp;
    dp[0] = 1;
    for (auto num : nums){
        unordered_map<int, int> temp;
        for (auto x : dp){
            int sum = x.first, count = x.second;
            temp[sum + num] += count;
            temp[sum - num] += count;
        }
        dp = temp;
    }
    return dp[S];
}

这样用了两个map,第一个map一直保持键值和键值和所对应的出现的次数,第二个map一直在循环中作为中间变量;

【问题分析】

1、该问题求解数组中数字只和等于目标值的方案个数,每个数字的符号可以为正或负(减整数等于加负数)。

2、该问题和矩阵链乘很相似,是典型的动态规划问题

3、举例说明: nums = {1,2,3,4,5}, target=3, 一种可行的方案是+1-2+3-4+5 = 3

 该方案中数组元素可以分为两组,一组是数字符号为正(P={1,3,5}),另一组数字符号为负(N={2,4})

 因此:    sum(1,3,5) - sum(2,4) = target

          sum(1,3,5) - sum(2,4) + sum(1,3,5) + sum(2,4) = target + sum(1,3,5) + sum(2,4)

          2sum(1,3,5) = target + sum(1,3,5) + sum(2,4)

          2sum(P) = target + sum(nums)

          sum(P) = (target + sum(nums)) / 2

 由于target和sum(nums)是固定值,因此原始问题转化为求解nums中子集的和等于sum(P)的方案个数问题

4、求解nums中子集合只和为sum(P)的方案个数(nums中所有元素都是非负)

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

int findTargetSumWays(vector<int>& nums, int S) {
    int sum = 0;
    for (auto x : nums)sum += x;
    return sum < S || (S + sum) & 1 ? 0 : subsetSum(nums, (S + sum) >> 1);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值