动态规划part06
- 完全背包
- 518. 零钱兑换 II
- 解题思路
- 377. 组合总和 Ⅳ
- 解题思路
详细布置
力扣上没有纯粹的完全背包的题目,所以大家看本篇了解一下 完全背包的理论
后面的两道题目,都是完全背包的应用,做做感受一下
完全背包
视频讲解: 完全背包
文章讲解: 完全背包
518. 零钱兑换 II
题目链接: 518. 零钱兑换 II
视频讲解: 518. 零钱兑换 II
文章讲解: 518. 零钱兑换 II
解题思路
一看到钱币数量不限,就知道这是一个完全背包
但本题和纯完全背包不一样,纯完全背包是凑成背包最大价值是多少,而本题是要求凑成总金额的物品组合个数!
动规五步曲:
- 确定dp数组以及下标的含义
dp[j]:凑成总金额j的货币组合数为dp[j] - 确定递推公式
dp[j] 就是所有的dp[j - coins[i]](考虑coins[i]的情况)相加。
所以递推公式:dp[j] += dp[j - coins[i]];
这个递推公式大家应该不陌生了,我在讲解01背包题目的时候在这篇494. 目标和中就讲解了,求装满背包有几种方法,公式都是:dp[j] += dp[j - nums[i]]; - dp数组如何初始化
dp[0] = 1 - 确定遍历顺序
本题中我们是外层for循环遍历物品(钱币),内层for遍历背包(金钱总额),还是外层for遍历背包(金钱总额),内层for循环遍历物品(钱币)呢?
我在动态规划:关于完全背包,你该了解这些!
中讲解了完全背包的两个for循环的先后顺序都是可以的。
但本题就不行了!
如果求组合数就是外层for循环遍历物品,内层for遍历背包。
如果求排列数就是外层for遍历背包,内层for循环遍历物品。 - 举例推导dp数组
// 动态规划
class Solution {public int change(int amount, int[] coins) {int[] dp = new int[amount + 1];dp[0] = 1;for(int i = 0; i < coins.length; i++){for(int j = coins[i]; j <= amount; j++){dp[j] += dp[j - coins[i]];}}return dp[amount];}
}
377. 组合总和 Ⅳ
题目链接: 377. 组合总和 Ⅳ
视频讲解: 377. 组合总和 Ⅳ
文章讲解: 377. 组合总和 Ⅳ
解题思路
和上道题相比,该题是排列问题,上道题是组合问题。只是遍历顺序有变化,其他基本一样。
如果求组合数就是外层for循环遍历物品,内层for遍历背包。
如果求排列数就是外层for遍历背包,内层for循环遍历物品。
如果把遍历nums(物品)放在外循环,遍历target的作为内循环的话,举一个例子:计算dp[4]的时候,结果集只有 {1,3} 这样的集合,不会有{3,1}这样的集合,因为nums遍历放在外层,3只能出现在1后面!
所以本题遍历顺序最终遍历顺序:target(背包)放在外循环,将nums(物品)放在内循环,内循环从前到后遍历。
// 动态规划
class Solution {public int combinationSum4(int[] nums, int target) {int[] dp = new int[target + 1];dp[0] = 1;for(int i = 0; i <= target; i++){ // 排列问题 外层遍历背包for(int j = 0; j < nums.length; j++){ // 内层遍历物品if(i >= nums[j]){dp[i] += dp[i - nums[j]];}}}return dp[target];}
}