代码随想录算法训练营第四十四天|完全背包、518. 零钱兑换 II、377. 组合总和 Ⅳ
完全背包
完全背包
文章讲解:https://programmercarl.com/%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80%E5%AE%8C%E5%85%A8%E8%83%8C%E5%8C%85.html
题目链接:https://kamacoder.com/problempage.php?pid=1052
视频讲解:https://www.bilibili.com/video/BV1uK411o7c9/
自己看到题目的第一想法
完全背包是指背包里的物品能随便用几次。01背包的话背包里的物品是能使用一次。
看完代码随想录之后的想法
完全背包是指背包里的物品能随便用几次。01背包的话背包里的物品是能使用一次。
完全背包和01背包的区别:
- 遍历顺序
- 01背包遍历时,物品从小到大遍历,背包从大到小遍历。(逆序可以把dp数组可以打印出来,看一下dp数组状态是如何转移的。)硬想想不懂,还是打印出来能理解。
- 完全背包遍历时,物品从小到大遍历,背包也从小到大遍历。(正序也可以把dp数组可以打印出来,看一下dp数组状态是如何转移的。)
- 完全背包遍历时,还可以先遍历背包后遍历物品。
- 原因:先物后背从左到右,以行的方式更新。先背后物品,以列的方式更新。
自己实现过程中遇到哪些困难
零钱兑换 II
518. 零钱兑换 II
文章讲解:https://programmercarl.com/0518.%E9%9B%B6%E9%92%B1%E5%85%91%E6%8D%A2II.html
题目链接:https://leetcode.cn/problems/coin-change-ii/
视频讲解:https://www.bilibili.com/video/BV1KM411k75j
自己看到题目的第一想法
列出二维组合矩阵,查看一下规律。
dp定义给出来了,但是二维递推公式没想出来。
- dp数组定义:dp[i][j] 0-i个物品,在j的背包重量下,凑成重量j的硬币组合数
- 确定递推公式:二维递推公式没想出来(这里直接用一下别人的图,不拍自己纸上画的了。)
看完代码随想录之后的想法
没看懂,看leetcode官方题解去了:
- 确定dp定义:用dp[x]表示金额之和等于x的硬币组合数,目标是求dp[amount]。
- 确定递推公式:对于面额为coin的硬币,当coin <= i <= amount时,如果存在一种硬币组合的金额之和等于i - coin。则在该硬币组合中增加一个面额为coin的硬币,即可得到一种金额之和等于i的硬币组合。(所以遍历coin,然后拿到)
动态规划做法:
- 初始化dp[0] = 1;
- 遍历coins,对于其中每个coin进行如下操作:
- 遍历i从coin到amount,将dp[i-coin]的值加到dp[i]。
- 这里的递推公式推导
- 只要搞到nums[i],凑成dp[j]就有dp[j - nums[i]] 种方法。
- 例如:dp[j],j 为5,
- 已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 容量为5的背包。
- 已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 容量为5的背包。
- 已经有一个3(nums[i]) 的话,有 dp[2]中方法 凑成 容量为5的背包
- 已经有一个4(nums[i]) 的话,有 dp[1]中方法 凑成 容量为5的背包
- 已经有一个5 (nums[i])的话,有 dp[0]中方法 凑成 容量为5的背包
- 那么凑整dp[5]有多少方法就是把 所有的 dp[j - nums[i]] 累加起来。
- 最终得到dp[amount]的值即为答案。
自己实现过程中遇到哪些困难
递推公式的推导没理解
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]];
}
}
/**
coin:1,dp[1]:1
coin:1,dp[2]:1
coin:1,dp[3]:1
coin:1,dp[4]:1
coin:1,dp[5]:1
coin:2,dp[2]:2
coin:2,dp[3]:2
coin:2,dp[4]:3
coin:2,dp[5]:3
coin:5,dp[5]:4
*/
return dp[amount];
}
组合总和 Ⅳ
377. 组合总和 Ⅳ
文章讲解:https://programmercarl.com/0377.%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8C%E2%85%A3.html
题目链接:https://leetcode.cn/problems/combination-sum-iv/
视频讲解:https://www.bilibili.com/video/BV1V14y1n7B6/
自己看到题目的第一想法
这道题目取得是排列数,和上一题的组合数不一样,这里用的是排列数的话遍历的顺序不一致。
看完代码随想录之后的想法
coins[1,2,3]
target = 4
1+1+1+1
1+1+2
1+2+1
2+1+1
2+2
1+3
3+1
输出7
和零钱兑换的区别就是遍历顺序。
遍历顺序看了一遍还是看不懂
public int combinationSum4(int[] nums, int target) {
int[] dp = new int[target + 1];
dp[0] = 1;
for (int i = 0; i < nums.length; i++) { // 遍历物品
for (int j = nums[i]; j <= target; j++) { // 遍历背包容量
dp[j] += dp[j - nums[i]];
System.out.println("dp[" + j + "]:"+dp[j]);
}
}
/**
nums = [1,2,3] target=4
dp[1]:1
dp[2]:1
dp[3]:1
dp[4]:1
dp[2]:2
dp[3]:2
dp[4]:3
dp[3]:3
dp[4]:4
*/
// for (int j = 0; j <= target; j++) { // 遍历背包容量
// for (int i = 0; i < nums.length; i++) { // 遍历物品
// if (j - nums[i] >= 0) dp[j] += dp[j - nums[i]];
// System.out.println("dp[" + j + "]:"+dp[j]);
// }
// }
// /**
// dp[0]:1
// dp[0]:1
// dp[0]:1
// dp[1]:1
// dp[1]:1
// dp[1]:1
// dp[2]:1
// dp[2]:2
// dp[2]:2
// dp[3]:2
// dp[3]:3
// dp[3]:4
// dp[4]:4
// dp[4]:6
// dp[4]:7
// */
return dp[target];
}
今日收获&学习时长
后面再回顾