给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。
返回可以使最终数组和为目标数 S 的所有添加符号的方法数。
输入:nums: [1, 1, 1, 1, 1], S: 3
输出:5
输入:nums: [1, 0], S: 1
输出:2
输入:nums: [0, 0, 0, 0, 0, 0, 0, 0, 1], S: 1
输出:256
提示:
- 数组非空,且长度不会超过 20 。
- 初始的数组的和不会超过 1000 。
- 保证返回的最终结果能被 32 位整数存下。
'''
动态规划:初始和不超过1000
时间:O(N*sum), N为数组长度, sum为目标和的范围
空间:O(N*sum)
'''
class Solution(object):
def findTargetSumWays(self, nums, S):
if S > 1000:
return 0
# 要考虑正数、负数、零,因此这里有1000的偏置项
# dp[i][j]表示用前i个元素组成结果j的数目
dp = [[0] * 2001 for _ in range(len(nums))]
# 边界条件:第一个元素只使用一次+-符号组成结果j数目为1
dp[0][nums[0] + 1000] = 1
# 当nums[0]==0时,dp[0][0+1000]应该为2,而不是被赋值为1
dp[0][-nums[0] + 1000] += 1
for i in range(len(nums)-1):
for sums in range(-1000, 1001, 1):
if dp[i][sums + 1000] > 0:
dp[i+1][sums + nums[i+1] + 1000] += dp[i][sums + 1000]
dp[i+1][sums - nums[i+1] + 1000] += dp[i][sums + 1000]
return dp[-1][S + 1000]
这里构造DP数组:dp[i][j]表示用前 i 个元素组成结果 j 的数目
注意初始化DP数组时要考虑nums[0] == 0的情况
理解递推:构成“sums + nums[i+1] + 1000”或者“sums - nums[i+1] + 1000”结果,需要累加构成“sums + 1000”结果的数目
dp[i+1][sums + nums[i+1] + 1000] += dp[i][sums + 1000]
dp[i+1][sums - nums[i+1] + 1000] += dp[i][sums + 1000]
理解判断条件:只有当存在一些能够构成“sums + 1000”这个结果的运算组合时,才能由这个“sums + 1000”运算结果组合成其他新的结果;
if dp[i][sums + 1000] > 0:
从labuladong的算法小抄中看到很有意思的框架,贴在这里。。。
for 状态1 in 状态1的所有取值:
for 状态2 in 状态2的所有取值:
for ...
dp[状态1][状态2][...] = 择优(选择1, 选择2...)
在这里例子中:状态1遍历了整个nums列表中的每一个item;状态2考虑了所有可能存在的运算结果;