算法训练营day38 | {0.完全背包(纯), 518.零钱兑换 II, 377.组合总和 Ⅳ, 70. 爬楼梯 (进阶)}
第九章动态规划part05,完全背包应用,注意组合数、排列数在遍历顺序上的区别。
0. 完全背包(纯)
- 题目链接:52. 携带研究材料(第七期模拟笔试)
- 文档讲解:代码随想录
- 视频讲解:带你学透完全背包问题! 和 01背包有什么差别?遍历顺序上有什么讲究?
- 状态:与01背包的区别是,完全背包的每件物品可以使用无限次,01背包每个物品只能使用一次。
0.1 理论基础
有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。对于不考虑应用场景的纯粹01背包问题,先遍历物品或是先遍历背包都是可以的。
0.2 完全背包-二维数组
举个例子学习完全背包的DP五部曲(二维dp),例如背包最大重量为4,物品为:
| 重量 | 价值 | |
|---|---|---|
| 物品0 | 1 | 15 |
| 物品1 | 3 | 20 |
| 物品2 | 4 | 30 |
每件商品都有无限个,问背包能背的物品最大价值是多少?
动规五部曲:
- 确定dp数组以及下标的含义: dp[i][j]的定义:从下标为[0-i]的物品,每个物品可以取无限次,放进容量为j的背包,价值总和最大是多少。
- 确定递推公式: dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i])
- dp数组如何初始化: 从dp[i][j]的定义出发,如果背包容量j为0,dp[i][0] = 0; 取物品0时,当j < weight[0], dp[i][0] = 0, 当>= weight[0]时,就一直装,每一种物品就无限个。
- 确定遍历顺序: 先遍历物品还是先遍历背包都是可以的。
- 举例推导dp数组:

注意事项:
- 关于递推公式:不放物品i这里和01背包有区别了,空出物品i的容量之后,背包里原来仍有可能可以放物品i。
- 关于初始化:一定要和dp数组的定义吻合,否则到递推公式的时候就会越来越乱。
- 解题的时候,
j的遍历起点错了,当j < weight[i]时,没有给dp[i][j]赋值,但这些状态应该继承上一行 dp[i][j] = dp[i - 1][j]; - dp数组第一行的初始化不够通用,让递推统一处理更标准。
▶
完全背包-二维数组1 | |
0.3 完全背包-一维数组
将二维DP数组,压缩成一维DP数组,也就是将上一层拷贝到当前层。递推公式变为:
- dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
其实不管是dp数组定义、还是遍历顺序,在想的时候就和二维dp数组一样想,所以无所谓先遍历物品还是背包容量。
▶
完全背包-一维数组1 | |
518. 零钱兑换 II
- 题目链接:518. 零钱兑换 II
- 文档讲解:代码随想录
- 视频讲解:动态规划之完全背包,装满背包有多少种方法?组合与排列有讲究!| LeetCode:518.零钱兑换II
- 状态:完全背包应用题开始了,注意组合数是不考虑硬币的顺序的。
1.1 解题分析
硬币的价值就是硬币的重量,题目要求背包容量为amount,把背包装满有几种方案?不考虑硬币放入背包的顺序。每一种面值的硬币有无限个,典型的完全背包问题,dp数组的定义是关于方案个数的。
动规五部曲:
- 确定dp数组以及下标的含义: dp[i][j]的定义:从下标为[0~i]的硬币中任取无限次,容量为j的背包有dp[i][j]种装满的方式。
- 确定递推公式: dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i]]
- dp数组如何初始化:
- 第一列dp[i][0]: 当背包容量为0时,因为硬币coin[i]面值不可能为0,只有一种组合方案,就是不装,所以dp[i][0] = 1
- 第一行dp[0][j]: 当放硬币coin[0]时,如果 j能被coin[0]整除,那有1种方案,否则没有。
- 确定遍历顺序: 求组合数应该先遍历物品再正序遍历背包。
- 举例推导dp数组:
▶
518. 零钱兑换 II(二维dp)1 | |
压缩物品的维度,考虑一维dp数组。
动规五部曲:
- 确定dp数组以及下标的含义: dp[j]的定义:从下标为[0~i]的硬币中任取无限次,容量为j的背包有dp[j]种装满的方式。
- 确定递推公式: dp[j] = dp[j] + dp[j - coins[i]]
- dp数组如何初始化:
- 背包容量为0时,有1种方法装满,就是不装,dp[0] = 1
- 其他位置,因为要累加所以全部初始化为0
- 确定遍历顺序: 求组合数应该先遍历物品再正序遍历背包。
- 举例推导dp数组:
▶
518. 零钱兑换 II(一维dp)1 | |
377. 组合总和 Ⅳ
- 题目链接:377. 组合总和 Ⅳ
- 文档讲解:代码随想录
- 视频讲解:动态规划之完全背包,装满背包有几种方法?求排列数?| LeetCode:377.组合总和IV
- 状态:每个元素可以使用无限次,仍是完全背包问题,但dp数组本题要求记录排列数。
2.1 解题分析
DP五部曲:
- dp[j]数组定义:顺序不同,算不同方案,dp的语义必须是凑成j的排列数有dp[j]种
- 递推公式:
- 也就是说最后一个数是谁,是重要信息,假设最后一个数是num
- dp[j] += dp[j - num]
- dp数组初始化:
- dp[0] = 1
- 遍历顺序:因为是求排列数,先遍历背包,再遍历物品
- 打印dp数组。
▶
377. 组合总和 Ⅳ1 | |
70. 爬楼梯 (进阶)
- 题目链接:70. 爬楼梯 (进阶)
- 文档讲解:代码随想录
- 状态:之前做爬楼梯这题,在动规入门,还没有涉及到背包问题。
2.1 解题分析
题目描述:假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬至多m (1 <= m < n)个台阶。你有多少种不同的方法可以爬到楼顶呢?
你有一个背包容量为n,有物品重量从1到m,无限个,请问凑到容量n的排列数是几种?问题转化为完全背包的排列数。同样的,在维度1不能锁定物品出现的顺序,否则就是在算组合数。
DP五部曲:
- dp[j]数组定义:顺序不同,算不同方案,dp的语义必须是凑成j的排列数有dp[j]种
- 递推公式:
- 也就是说最后一个数是谁,是重要信息,假设最后一个数是num
- dp[j] += dp[j - num]
- dp数组初始化:
- dp[0] = 1
- 遍历顺序:因为是求排列数,先遍历背包,再遍历物品
- 打印dp数组。
2.2 解题小结
和377. 组合总和 Ⅳ是一模一样的。
▶
70. 爬楼梯 (进阶)1 | |
算法训练营day38 | {0.完全背包(纯), 518.零钱兑换 II, 377.组合总和 Ⅳ, 70. 爬楼梯 (进阶)}
http://paopaotangzu.xyz/cn/day38_leetcode/