leetcode(4)背包系列题目
背包递推公式
-
问能否能装满背包(或者最多装多少):dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]); ,对应题目如下:
-
322.零钱兑换
考过没做出来
初始化凑成每个金额都使用面额为1的硬币,则最多需要amount个硬币,因此初始化为amount + 1class Solution: def coinChange(self, coins: List[int], amount: int) -> int: dp = [amount + 1] * (amount + 1) # 都是amount + 1 dp[0] = 0 for c in coins: for j in range(c, amount + 1): dp[j] = min(dp[j], dp[j - c] + 1) return dp[amount] if dp[amount] < amount + 1 else -1 # 如果<就说明是由coins里面组成的
-
279.完全平方数
考过没做出来
就是物品(可以无限件使用),凑个正整数n就是背包,问凑满这个背包最少有多少物品?class Solution: def numSquares(self, n: int) -> int: dp = [10**4] * (n + 1) # 初始化背包装每个重量时的最多物品个数 dp[0] = 0 # 第0个开始 nums = [] i = 1 while i*i <= n: # 初始化每个物品的重量 nums.append(i*i) i += 1 for num in nums: for j in range(num,n + 1): dp[j] = min(dp[j], dp[j - num] + 1) return dp[-1]
-
总结:动态规划-我的一生之敌...(不是)转移方程一定要想清楚!!!
一、01背包问题
如果使用二维dp数组,先遍历物品还是先遍历背包重量都可以,且两层循环都是正序遍历!
如果使用一维dp数组,只能是物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历!
a.内层循环遍历顺序
二维dp遍历的时候,背包容量是从小到大,而一维dp遍历的时候,背包是从大到小。
因为倒序遍历是为了保证物品i只被放入一次!。但如果一旦正序遍历了,那么物品0就会被重复加入多次!
举一个例子:物品0的重量weight[0] = 1,价值value[0] = 15
如果正序遍历
dp[1] = dp[1 - weight[0]] + value[0] = 15
dp[2] = dp[2 - weight[0]] + value[0] = 30
此时dp[2]就已经是30了,意味着物品0,被放入了两次,所以不能正序遍历。
为什么倒序遍历,就可以保证物品只放入一次呢?
倒序就是先算dp[2]
dp[2] = dp[2 - weight[0]] + value[0] = 15 (dp数组已经都初始化为0)
dp[1] = dp[1 - weight[0]] + value[0] = 15
所以从后往前循环,每次取得状态不会和之前取得状态重合,这样每种物品就只取一次了。
那么问题又来了,为什么二维dp数组历的时候不用倒序呢?
因为对于二维dp,dp[i][j]都是通过上一层即dp[i - 1][j]计算而来,本层的dp[i][j]并不会被覆盖!
b.两个嵌套for循环的顺序
只能是物品遍历的for循环放在外层,遍历背包的for循环放在内层。
因为一维dp的写法,背包容量一定是要倒序遍历,如果遍历背包容量放在上一层,那么每个dp[j]就只会放入一个物品,即:背包里只放入了一个物品。
二、完全背包问题
完全背包和01背包问题唯一不同的地方就是,每种物品有无限件。
a.内层循环遍历顺序
01背包内嵌的循环是从大到小遍历,为了保证每个物品仅被添加一次。
而完全背包的物品是可以添加多次的,所以要从小到大正序遍历
b.两个嵌套for循环的顺序
在完全背包中,对于一维dp数组来说,其实两个for循环嵌套顺序是无所谓的!
因为dp[j] 是根据 下标j之前所对应的dp[j]计算出来的。 只要保证下标j之前的dp[j]都是经过计算的就可以了。
三、求装满背包有几种方法
初始化为:dp[0]=0,而前面是:dp[0]=1
递推公式:dp[j] += dp[j - nums[i]]
- 如果求组合数就是外层for循环遍历物品,内层for遍历背包。
- 如果求排列数就是外层for遍历背包,内层for循环遍历物品。
- 如果求最小数,那么两层for循环的先后顺序就无所谓了。
求组合数:动态规划:518.零钱兑换II
求排列数:动态规划:377. 组合总和 Ⅳ 、动态规划:70. 爬楼梯进阶版(完全背包)
求最小数:动态规划:322. 零钱兑换 、动态规划:279.完全平方数 (递推公式为:dp[j]=min(dp[j],dp[j - nums[i]]+ 1))
四、多重背包问题
多重背包介于完全背包和01背包问题之间,即每种物品的个数是指定的。
参考资料:
背包问题总结篇
一篇文章吃透背包问题!
一文搞懂完全背包问题