题目描述:
给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。返回可以使最终数组和为目标数 S 的所有添加符号的方法数。
示例 1:
输入: 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位整数存下。
解题思路:
本题目是一个标准动态规划思想,可以每次进行加减时记录当时的状态(加减和及对应的方案数)。下一次加减一个数字时的状态可以参考上个状态计算得出,再下一次加减一个数字这个状态又变成。最后根据最终状态输出目标和S的方案数。
样例:输入nums=[0,1,0,1], S=0;输出方案数ans

python3 Solution:
class Solution:
def findTargetSumWays(self, nums: List[int], S: int) -> int:
# 若列表为空,和为0就返回方案数1
if not nums and S == 0:
return 1
# 若列表为空,和不为0就返回方案数0
elif not nums:
return 0
# 若列表不为空,和不为0的情况
else:
# 数组长度后面遍历使用
length = len(nums)
# 初始化第一个数字的状态,res代表上一个状态,因为后面我们从第二个数开始遍历
if nums[0]==0:
# 若第一个数字是0,那就初始化字典为{"0":2},此时和为0有两个方案:+0和-0
res = {nums[0]:2}
else:
# 若第一个数字不是0,假设是1,那就初始化字典为{"1":1, "-1":1}
res = {nums[0]:1, -nums[0]:1}
# 遍历后面每一个数字
for i in range(1, length):
# 临时字典,存储当前状态
tmp = {}
# 遍历上个状态
keys = res.keys()
for key in keys:
# 上个状态的和加减当前数,代表当前某个状态和
tmp_add = int(key) + nums[i]
tmp_sub = int(key) - nums[i]
# 若当前这个状态和在当前状态字典tmp中,那就把当前方案数加上上个状态中key状态的方案数
'''
例如res = {"1":4,"-1":4},当前数为1,res的第一个key("1")加上1,减去1放到当前状态中,此时tmp = {"2":4, "0":4},res的第二个key("-1")加上1等于0,tmp里面已经有0了,所以现在用res["-1"]+tmp["0"]更新tmp["0"](ps:最开始的tmp的状态和"0"来自res的状态和"1"减1,现在的0来自于res的状态和"-1"加1)
'''
if tmp_add in tmp:
tmp[tmp_add] += res[key]
# 若当前这个状态和不在当前状态字典tmp中,就把上个状态中的key状态和的方案数当作当前这个方案和的方案数,更新到当前状态的字典中
else:
tmp.update({tmp_add:res[key]})
# 和上面相同,这个是减去该数
if tmp_sub in tmp:
tmp[tmp_sub] += res[key]
else:
tmp.update({tmp_sub:res[key]})
# 把当前状态赋值给上个状态,开始遍历下个数
res = tmp
# 若最后状态中存在该加减和,则取出方案数,否则方案数为0
return res.get(S, 0)
复杂度分析
时间复杂度:O(N∗sum),其中 N 是数组 nums 的长度。
空间复杂度:O(sum),sum为状态字典的长度。
写在最后:
本方法是自己看到这道题想到的解题方法(代码方面还有优化空间),写该文章主要是记录自己的思路,同时和需要的朋友进行分享。当然还有更精简的标准答案,就不在这里一一列出。