494 目标和

本文最后更新于:2021年8月14日 上午

给定一个非负整数数组,a1, a2, …, an, 和一个目标数,S。现在你有两个符号 +-。对于数组中的任意一个整数,你都可以从 +-中选择一个符号添加在前面。

返回可以使最终数组和为目标数 S 的所有添加符号的方法数。

示例:

1
2
3
4
5
6
7
8
9
10
11
输入: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 位整数存下。

Solution

参考:《算法小抄》2.19

  • 递归算法,超时
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# @lc code=start
class Solution:
def findTargetSumWays(self, nums: List[int], S: int) -> int:
if len(nums)==0: return 0
def backtrack(nums, i, rest):
result=0
if i==len(nums):
if rest==0:
return 1
return 0
rest += nums[i] # 选择 - 号
result+= backtrack(nums, i+1, rest)
rest -= nums[i]

rest -= nums[i] # 选择 + 号
result+=backtrack(nums, i+1, rest)
rest += nums[i]
return result

return backtrack(nums, 0, S)
# @lc code=end

参考 carlsun-2

  • 转换成 0-1 背包问题

    把所有符号为正的数总和设为一个背包的容量,容量为x;把所有符号为负的数总和设为一个背包的容量,容量为y。在给定的数组中,有多少种选择方法让背包装满。令sum为数组的总和,则x+y = sum。而两个背包的差为S,则x-y=S。从而求得x=(S+sum)/2。
    基于上述分析,题目转换为背包问题:给定一个数组和一个容量为x的背包,求有多少种方式让背包装满。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int S) {
int n = nums.size();
int sumAll = 0;
for(int num : nums)
sumAll += num;
if(sumAll<S || (sumAll+S)%2==1)
return 0;

int sum = (sumAll+S)/2;
vector<int> dp(sum+1, 0);
dp[0] = 1;
for(int i=0; i<n; ++i){
for(int j=sum; j>=nums[i]; --j)
dp[j] += dp[j-nums[i]];
}
return dp[sum];
}
};

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!