代码随想录算法训练营第四十三天| 完全背包 518. 零钱兑换 II 377. 组合总和 Ⅳ

文档讲解:代码随想录

视频讲解:代码随想录B站账号

状态:看了视频题解和文章解析后做出来了

完全背包

class Solution:
    def completeBag(self, weight, value, bagweight):
        dp = [0] * (bagweight + 1)
        
        # 遍历物品,从0开始
        for i in range(len(weight)):
            # 物品重复使用,但从背包重量等于物品重量开始遍历,因为小于是装不进去的
            for j in range(weight[i], bagweight + 1):
                dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
                
        return dp[bagweight]
  • 时间复杂度:O(n^2)
  • 空间复杂度:O(n)

完全背包,和01背包唯一的区别就是每件物品可以重复无限次放入背包中。

动态规划五部曲:

1. 确定dp数组以及下标的含义:dp[j]代表背包重量为j时可以放入的最大价值

2. 确定递推公式:

完全背包的递推公式同样为:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

3. dp数组初始化:

dp全部初始化为0,因为遍历途中有max和相加的操作,所以赋值为非负数的最小整数0.

如果题目给的价值有负数,那么非0下标就要初始化为负无穷。

4. 确定遍历顺序:

在这道题中,遍历顺序其实无所谓。先遍历背包还是先遍历物品都行,遍历背包从前往后和从后往前也都可以。

- 为什么有些题只能先遍历物品?

一维的解决01背包的时候,如果先从后往前遍历背包,那么每个背包只会被放入一件物品。因为每个背包只被总体循环遍历过一次。

- 为什么有些题需要背包从后往前遍历

一维解决01背包的时候,如果从前往后遍历,那么每个物品就可以使用多次了。

5. 举例推导dp数组

518. 零钱兑换 II

class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        dp = [0] * (amount + 1)
        dp[0] = 1

        for i in range(len(coins)):
            for j in range(coins[i], amount+1):
                dp[j] += dp[j-coins[i]]

        return dp[amount]

完全背包问题,代码思路和494.目标和完全一样,这里不再赘述了。

377. 组合总和 Ⅳ

class Solution:
    def combinationSum4(self, nums: List[int], target: int) -> int:
        dp = [0] * (target + 1)
        for i in range(1, target+1):
            for n in nums:
                if n == i:
                    dp[i] += 1
                elif n < i:
                    dp[i] += dp[i-n]
                    
        return dp[-1]

五部曲也类似与完全背包问题,但这道题要求结果是排列的数量。排列就意味着顺序也必须考虑在内,[1, 2] 和 [2, 1]是两个不同的排列方法。

这时候我们的遍历顺序就必须先从背包开始遍历,再从物品遍历。这点要解释起来非常抽象,通过打印两种方法的dp数组更新逻辑来看:

 第二种是先背包再物品,可以看到第五个数组[1,1,2,0,0,0],这里的2其实包含两种置放方法,一种是[1,1],一种是[2]。再到第七个数组[1,1,2,2,0,0],第二个2其实包含了[1,1,1]和[2,1]两种方法。第八个数组的3(也就是多的那个1)包含[1,2]这个组合。

这样[1,2]和[2,1]两种排列就都计入其中了。

从前往后遍历还是因为是完全背包问题,元素可以重复使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值