1. 题目
给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。
返回可以使最终数组和为目标数 S 的所有添加符号的方法数。
2. 解题思路
- 回溯法
数组中每个值都有两种选择,乘1或者乘-1,通过递归的方式回溯,遍历每一种情况,如果符合条件将结果加1
时间复杂度太高,会超时 - 动态规划
假设所有取正的数字之和为x,所有取负的数字之和为y,则
\(target=x-y\)
\(sum=x+y\)
\(x=\frac{sum+target}{2}\)
此时问题变为01背包问题,背包容量为\(\frac{sum+target}{2}\),即在背包中取出该大小的东西的组合数
令dp[j]为 填满j容量的背包,有dp[j]种方法- 初始化:dp[0]=1,即填满容量为0的背包有一种组合方式
- 状态转移方程:dp[i] = dp[i] + dp[i - num]
当前填满容量为j的包的方法数 = 之前填满容量为j的包的方法数 + 之前填满容量为j - num的包的方法数,也就是当前数num的加入,可以把之前和为j - num的方法数加入进来。
3. 代码
- 回溯法
class Solution:
def __init__(self):
self.res = 0
self.op = [1, -1]
def findTargetSumWays(self, nums, target: int) -> int:
self.back(nums, target, 0, 0)
return self.res
def back(self, nums, target, index, total):
if index == len(nums) and total == target:
self.res += 1
else:
if index != len(nums):
for i in self.op:
total += i * nums[index]
self.back(nums, target, index+1, total)
total -= i * nums[index]
- 动态规划
class Solution:
def findTargetSumWays(self, nums, target: int) -> int:
# target=x-y
# sum=x+y
# x=(sum+target)/2
x = (sum(nums) + target) // 2
if target > sum(nums) or (target + sum(nums)) % 2:
return 0
dp = [0] * (x + 1)
# 填满容量为j的背包,有dp[j]种方法
dp[0] = 1
for num in nums:
for i in range(x, num - 1, -1):
# 当前填满容量为j的包的方法数 = 之前填满容量为j的包的方法数 + 之前填满容量为j - num的包的方法数也就是当前数num的加入,可以把之前和为j - num的方法数加入进来。
dp[i] = dp[i] + dp[i - num]
return dp[-1]