经典动态规划:完全背包问题 :: labuladong的算法小抄 #1017
Replies: 44 comments 17 replies
-
base case代码里没写dp[0][j]的情况 |
Beta Was this translation helpful? Give feedback.
-
Java中int数组创建后所有元素都默认是0,所以为0的base case就不需要再初始化了。 |
Beta Was this translation helpful? Give feedback.
-
如果把 第 i 物品装入背包的情况下, dp[i][j] 不是应该等于dp[i-1][j-coins[i-1]] 吗? |
Beta Was this translation helpful? Give feedback.
-
大佬,想问一下,为什么进行空间优化的时候,子集背包那题,j 需要从后往前遍历,而这里不需要呢? |
Beta Was this translation helpful? Give feedback.
-
@NiceMartin 如果把 第 i 物品装入背包的情况下, dp[i][j] 不是应该等于dp[i-1][j-coins[i-1]] 吗? 我也这么觉得。如果你决定使用这个面值的硬币,那么就应该关注如何凑出金额 j - coins[i-1]。 比如说,你想用面值为 2 的硬币凑出金额 5,那么如果你知道了凑出金额 3 的方法,再加上一枚面额为 2 的硬币,不就可以凑出 5 了嘛。 所以先dp[i-1][j-coins[i-1]] ,再加上这个面值的硬币。组合数不变。 不过从二维优化到一维之后是一样的。 |
Beta Was this translation helpful? Give feedback.
-
但是把dp[i][j - coins[i-1]]改成 dp[i-1][j-coins[i-1]] ,提交是错的! |
Beta Was this translation helpful? Give feedback.
-
这里每个硬币可以选用无限多次,即物品的数量是无限的,所以第 |
Beta Was this translation helpful? Give feedback.
-
@tiertie 关于空间优化的问题,在网站搜索「对动态规划进行降维打击」这篇文章,有详细解释。 |
Beta Was this translation helpful? Give feedback.
-
感谢大佬的答复,我终于明白了。我理解的是这样的: dp[i][j]中使用 i 的次数是 [0, +∞],当不使用 i 的时候,dp[i][j]=dp[i-1][j]; |
Beta Was this translation helpful? Give feedback.
-
压缩状态是我看漏了哪一章吗大佬,有点卡不懂压缩状态 |
Beta Was this translation helpful? Give feedback.
-
将凑零钱II 和 凑零钱I 格式统一
public int coinChange(int[] coins, int amount) {
int[][] dp = new int[coins.length+1][amount+1];
// coin = 0 没有钱
for(int j = 1 ; j <=amount;j++){
dp[0][j] = Integer.MAX_VALUE;
}
//amount = 0 要凑的钱 为 0
for(int i = 0; i<=coins.length;i++){
dp[i][0] = 0;
}
for(int i = 1; i<=coins.length;i++){
for(int j = 1; j<=amount;j++){
//注意这里需要对 Interger.MAX_VALUE 这个边界初始值进行判断
if( j >= coins[i-1] && dp[i][j-coins[i-1]]!=Integer.MAX_VALUE ){
dp[i][j] = Math.min(dp[i-1][j],dp[i][j-coins[i-1]]+1);
}else{//面额大于 要凑的钱
dp[i][j] = dp[i-1][j];
}
}
}
return dp[coins.length][amount] == Integer.MAX_VALUE ? -1: dp[coins.length][amount];
}
}
class Solution {
// dp[i][j] 表示 将i个硬币装入背包 , 剩余 需要凑的钱(j)是多少
public int change(int amount, int[] coins) {
int[][] dp = new int[coins.length+1][amount+1];
//base case
for(int i = 0; i<= coins.length;i++){
dp[i][0] = 1;//base case 就是要凑 0 元 说明直接每种硬币都能拿到一种可能性 就是啥也不装
}
for(int i = 1; i<=coins.length; i++){
for(int j = 1;j<=amount; j++){
if(j >= coins[i-1]){//要凑的钱 比 当前的硬币面值大
int a = dp[i-1][j];//依赖于前一种 当前不拿硬币 就剩余要凑 j 了
int b = dp[i][j-coins[i-1]];//当前拿了硬币 装入硬币了 就剩余要凑 j-coins[i-1]
dp[i][j] = a+b;//因为要求的是总共多少可能 组合数 需要将两种情况的组合数相加
}else{//剩余要凑的 j 元 比 当前的硬币面值还要小 说明凑不出来 只能依赖前一种
dp[i][j] = dp[i-1][j];
}
}
}
return dp[coins.length][amount];
}
} |
Beta Was this translation helpful? Give feedback.
-
@zhongweiLeex 给你的思考点赞👍 |
Beta Was this translation helpful? Give feedback.
-
@Tokics 这恰恰就是01背包和完全背包的区别 |
Beta Was this translation helpful? Give feedback.
-
@labuladong 东哥,这里有笔误哇? 如果你把这第 i 个物品装入了背包,也就是说你使用 coins[i] (应该是coins[i-1]?)这个面值的硬币,那么 dp[i][j] 应该等于 dp[i][j-coins[i-1]]。” |
Beta Was this translation helpful? Give feedback.
-
我觉得dp的定义没有太说清楚。按照题解,dp[i][j]定义为前i种硬币中,能凑出j金额的总数。那么我们可以划分为两种情况:dp[i][j] = 前i-1种硬币能凑出j金额的方法(第i种硬币不选)dp[i-1][j] + 至少有一枚第i种硬币 dp[i][j-nums[i]],即分为了第i 种硬币 = 0 和第 i 种硬币 >=1 两种子集,可以覆盖完全。 |
Beta Was this translation helpful? Give feedback.
-
@labuladong |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
对比01背包问题和完全背包问题一个细节 |
Beta Was this translation helpful? Give feedback.
-
对于“第三步”状态转移,分享下自己的思考:
|
Beta Was this translation helpful? Give feedback.
-
我有另一种DP的思路,在每个状态下,选择是当前index的coin使用的数量,从0开始,直到remain<0结束,感觉整体的状态树是一样的,但是leetcode运行效率却差很多,求东哥指点: class Solution {
int[] coins;
Integer[][] memo;
public int change(int amount, int[] coins) {
this.coins = coins;
this.memo = new Integer[amount + 1][coins.length];
return dp(amount, 0);
}
private int dp(int remain, int i){
if (remain == 0){
return 1;
}
if(i == coins.length){
return 0;
}
if(memo[remain][i] != null){
return memo[remain][i];
}
int res = 0;
for(int num = 0; ;num++){
int newRemain = remain - num * coins[i];
if(newRemain < 0){
break;
}
res += dp(newRemain, i+1);
}
memo[remain][i] = res;
return res;
}
} |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
大佬,能讲下爬楼梯的动态规划解法么?这种顺序有关的和零钱问题的区别在哪? |
Beta Was this translation helpful? Give feedback.
-
思路描述的有点问题,上面说放入硬币i的dp[i][j] = dp[i][j-coins[i-1]] |
Beta Was this translation helpful? Give feedback.
-
def change2(coins: List[int],
不知道这段代码错在哪了,跑的结果就是不对 |
Beta Was this translation helpful? Give feedback.
-
对于零钱兑换II 我第一反应是用回溯算法的框架来解决,因为是一个典型的无重复可复选的组合/子集问题,但是超时,希望有人能给优化一下。 |
Beta Was this translation helpful? Give feedback.
-
dp[i][j] = dp[i - 1][j] |
Beta Was this translation helpful? Give feedback.
-
我的学习进度是先掌握基本的,空间压缩后面再集中起来改写: java版空间二维dp table 压缩之前的代码,声明初始化时掉了 一个new。 |
Beta Was this translation helpful? Give feedback.
-
想了半天好像理解了,之前的01背包用 |
Beta Was this translation helpful? Give feedback.
-
我有一个关于dp数组初始化的问题 即 |
Beta Was this translation helpful? Give feedback.
-
文章链接点这里:经典动态规划:完全背包问题
评论礼仪 见这里,违者直接拉黑。
Beta Was this translation helpful? Give feedback.
All reactions