经典动态规划:完全背包问题 :: 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.
-
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.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
文章链接点这里:经典动态规划:完全背包问题
评论礼仪 见这里,违者直接拉黑。
Beta Was this translation helpful? Give feedback.
All reactions