Leetcode 494.目标和
1 题目描述(Leetcode题目链接)
给定一个非负整数数组,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
一共有5种方法让最终目标和为3。
注意:
- 数组非空,且长度不会超过20。
- 初始的数组的和不会超过1000。
- 保证返回的最终结果能被32位整数存下。
2 题解
最先想到的方法是递归方法,但是提交的时候直接就超时了。进行一些适当的变换,将其转换为01背包问题来做貌似才是此题的本意。
首先设
P
P
P是
n
u
m
s
nums
nums中所有前面加正号的数的集合,
N
N
N是
n
u
m
s
nums
nums中所有前面加负号的数的集合,那么我们就像要找到满足下式的集合数量(即
P
P
P的数量):
S
=
s
u
m
(
P
)
−
s
u
m
(
N
)
S = sum(P)-sum(N)
S=sum(P)−sum(N)
现在左右两边同时加上
s
u
m
(
P
)
+
s
u
m
(
N
)
sum(P) + sum(N)
sum(P)+sum(N)得到:
S
+
s
u
m
(
P
)
+
s
u
m
(
N
)
=
2
∗
s
u
m
(
P
)
S + sum(P) + sum(N) = 2*sum(P)
S+sum(P)+sum(N)=2∗sum(P)
因此:
s
u
m
(
P
)
=
(
S
+
s
u
m
(
n
u
m
s
)
)
/
2
sum(P) = (S+sum(nums))/2
sum(P)=(S+sum(nums))/2
那么问题就转化为:
n
u
m
s
nums
nums中有多少个子集满足其和等于
s
u
m
(
P
)
sum(P)
sum(P),也就是01背包问题即向容量为
s
u
m
(
P
)
sum(P)
sum(P)的背包里面装东西,有多少种方法恰好能装满。为此我们定义
D
P
DP
DP数组长度为
s
u
m
(
P
)
+
1
sum(P) + 1
sum(P)+1,
D
P
[
i
]
DP[i]
DP[i]表示当容量为
i
i
i,装前
j
j
j个物品的方法数,因此状态转移方程为:
D
P
[
i
]
=
D
P
[
i
]
+
D
P
[
i
−
n
u
m
s
[
j
]
]
DP[i] = DP[i] + DP[i-nums[j]]
DP[i]=DP[i]+DP[i−nums[j]]
class Solution:
def findTargetSumWays(self, nums: List[int], S: int) -> int:
if (S + sum(nums))%2 == 1 or sum(nums) < S:
return 0
capacity = (S + sum(nums))//2
DP = [0] * (capacity + 1)
DP[0] = 1
for j in range(len(nums)):
for i in range(capacity, -1, -1):
if i < nums[j]:
break;
DP[i] += DP[i - nums[j]]
return DP[capacity]