'''
Description: 494.目标和
Autor: 365JHWZGo
Date: 2021-11-23 09:17:29
LastEditors: 365JHWZGo
LastEditTime: 2021-11-23 20:24:54
'''
在看到题目时的想法:
一、在没有到达target之前都在相加,在超过之后回退到恰好比它小的数的位置,当时当你实际运用起来时,你会发现它行不通!
二、分为两个数组,一个是在数字之前添加+号,另一个数组是在数字之前添加-号
那么只需要知道有多少种组合能组合为差值,结果就为多少
假设‘➕’一组为A,’➖‘号一组为B
A+B=sum(nums)
A-B=target
所以:
A=(sum(nums)+target)//2
同时也知道,当A无法整除时,则不存在这样一种组合,适合A-B=target,所以结果为0
并且当A<0时,情况同上
思路:
1.确定dp数组以及含义
dp[j]表示在和为j的情况下,有dp[j]种方法
2.确定递推公式
dp[j]+=dp[j-nums[i]]
3.初始化dp数组
在和为0的情况下,要取决于是否数组中含有0,如果有的话则dp[0]=2^n(n为0的个数)
若是没有0的话,则dp[0]=2^0
4.dp数组的遍历顺序
一维dp的遍历,nums放在外循环,target在内循环,且内循环倒序
5.举例推导
EG:nums=[1,2,1],target=2
j 0 1 2
nums[0] 1 1 0
nums[1]. 1 1 1
nums[2]. 1 2 2
class Solution(object):
def findTargetSumWays(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
totalSum = sum(nums)
if (totalSum+target) % 2 != 0 or totalSum+target<0:
return 0
newTarget = (totalSum+target)//2
dp = [0 for _ in range(newTarget+1)]
#因为dp[j]的含义是当和为j时的有dp[j]种组合
#所以在初始化dp[0]时,需要考虑0的情况,因为0比较特殊,它+0和-0均不会影响最后的结果
dp[0] = 2**(nums.count(0))
for i in range(len(nums)):
#当nums[i]==0时,需要跳过,因为dp[j]+=dp[j-nums[i]]不需要再加自己
if nums[i]==0:
continue
else:
#因为已经考虑过0的情况了,所以只需要遍历[1,newTarget]
for j in range(newTarget,0,-1):
if j-nums[i]<0:
continue
dp[j]+=dp[j-nums[i]]
#print(dp)
return dp[-1]
另一种简洁版本,不需要考虑太多外在因素。
class Solution(object):
def findTargetSumWays(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
totalSum = sum(nums)
if (totalSum+target) % 2 != 0 or totalSum+target<0:
return 0
newTarget = (totalSum+target)//2
dp = [0 for _ in range(newTarget+1)]
dp[0]=1
for i in range(len(nums)):
for j in range(newTarget,nums[i]-1,-1):
dp[j]+=dp[j-nums[i]]
print(dp)
return dp[-1]
感悟:
这道题今天耗费了很长时间,主要是一直没推出来递推公式,思路什么的倒是好想,看了解题思路后,豁然开朗.