【leetcode题解——动态规划之0-1背包】494.目标和

267 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

题目链接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五部曲:(滚动数组)

  1. **dp[j]:**填满容量为j的背包,有dp[j]种方法

  2. 迭代公式:

    针对背包容量为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]]表示选)
  1. 初始化:

    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]时,转换为下标为0i-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的背包的方法数
  1. 遍历方向:

    i在外层,容量j在内层,内层方向遍历(防止dp被提前修改)

  2. 列举推导

    nums=[1,1,1,1,1] target=3

    正数=(sum+target)/2=(5+3)/2=4 即背包容量为4

1821650969643_.pic.jpg

得到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]