本文已参与「新人创作礼」活动,一起开启掘金创作之路。
题目链接:494.目标和
题目梳理:
选择用0-1背包的原因: 每个整数nums[i]只能用一次
两个关键变量:sum (nums数组元素之和)、target
为了达成target,数组要分为正数部分和负数部分,而可以根据sum和target求出要分为正数的数组和
正数+绝对值(负数)=sum
正数 - 绝对值(负数)=target
可以得到:正数=(sum+target)/2
背包问题转换为:求填满容量为(sum+target)/2的背包的方法个数
特殊情况:
- target>sum:无解
- (sum+target)/2不能整除:无解
- target为负数:target取绝对值
dp五部曲:(滚动数组)
-
**dp[j]:**填满容量为j的背包,有dp[j]种方法
-
迭代公式:
针对背包容量为5时:
当背包已经有1(nums[i])时,此时需要填满容量4,需要dp[4]种方法能填满容量为5的背包
当背包已经有2时,此时需要填满容量3,需要dp[4]种方法能填满容量为5的背包
...
当背包已经有5时,此时需要填满容量0,需要dp[0]种方法能填满容量为5的背包
即:dp[5]为所有容量情况的dp之和
dp[j]+=dp[j-nums[i]] #(dp[j]表示不选 dp[j-nums[i]]表示选)
-
初始化:
dp[0]=1 表示填满容量为0的背包有一种方法
其他元素初始化为0
从二维数组dp[i][j]的角度理解:
dp[i][j]表示使用下标0~i的nums[i]填满容量为j的背包有dp[i][j]种方法
此时dp[i][0]=1 容量为0的背包不管用num哪些元素都只有一种方法
二维数组迭代公式:
dp[i][j]=dp[i-1][j]+dp[i-1][j-nums[i]]
解释:
- 不选择nums[i]时,转换为下标为0~i-1的nums填满容量为j的背包的方法,即dp[i-1][j]
- 选择nums[i]时,转换为下标0~i-1的nums填满容量为j-nums[i]的背包的方法,即dp[j-nums[i]]
将这两种情况相加,则为使用下标0~i的nums[i]填满容量为j的背包的方法数
-
遍历方向:
i在外层,容量j在内层,内层方向遍历(防止dp被提前修改)
-
列举推导
nums=[1,1,1,1,1] target=3
正数=(sum+target)/2=(5+3)/2=4 即背包容量为4
得到dp[4]即:填满容量为4的背包有5种方法
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
ssum=sum(nums)
if target>ssum or (ssum+abs(target))%2:
return 0
#背包容量
weight=(ssum+abs(target))//2
#初始化
dp=[0]*(weight+1)
dp[0]=1
for i in range(len(nums)):
for j in range(weight,nums[i]-1,-1):
dp[j]+=dp[j-nums[i]]
return dp[weight]