完全背包问题
完全背包问题简介🐳
有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。
完全背包和01背包问题唯一不同的地方就是,每种物品有无限件。
问题特征🤓
与01背包和多重背包不同的是,完全背包问题中的每个物品数量都是无限的
思路转换😆
在01背包的滚动数组实现代码中,我们发现如果将bagSize的遍历按顺序遍历,而不是倒序,那么前面的dp[0]会被添加多次,这就是完全背包问题的解决方式
代码实现🚁
1、dp数组的含义:dp[i]表示bagSize=i时,背包中装入物品达到最大价值
2、递推公式:dp[j]=Math.max(dp[j],dp[j-weight[i]]+values[i])
3、数组的初始化:dp[0]=0,表示当bagSize=0时,背包中最大的价值为0,即装不下任何东西
4、遍历顺序:先遍历背包或者先遍历物品均可(但是求方法数量时,两者有较大区别)
5、测试一遍
//先遍历物品,再遍历背包
private static void testCompletePack(){
int[] weight = {1, 3, 4};
int[] value = {15, 20, 30};
int bagWeight = 4;
int[] dp = new int[bagWeight + 1];
for (int i = 0; i < weight.length; i++){ // 遍历物品
// 遍历背包容量!!!这里可以直接让j初始值为weight[i]
for (int j = weight[i]; j <= bagWeight; j++){
dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
}
}
for (int maxValue : dp){
System.out.println(maxValue + " ");
}
}
//先遍历背包,再遍历物品
private static void testCompletePackAnotherWay(){
int[] weight = {1, 3, 4};
int[] value = {15, 20, 30};
int bagWeight = 4;
int[] dp = new int[bagWeight + 1];
for (int i = 1; i <= bagWeight; i++){ // 遍历背包容量
for (int j = 0; j < weight.length; j++){ // 遍历物品
if (i - weight[j] >= 0){
dp[i] = Math.max(dp[i], dp[i - weight[j]] + value[j]);
}
}
}
for (int maxValue : dp){
System.out.println(maxValue + " ");
}
}
拓展:关于完全背包求解决方法的 数量 问题😬
问题特征:
求装满背包有几种方法?
解决方法:
这种问题我们不在将dp[j]的意义作为背包容量为j时可以放入的物品的最大价值,而是将其意义定义为背包容量为j可以装满背包的方法的数量!!!
值得注意的一点是,由于完全背包问题的遍历比较特殊,当两层for循环颠倒时,分别表示排列、组合情况下方法的数量,两者并不相同。
关于排列组合的解释
1、外层背包,内层物品:排列🥘
代码的实现:
//先遍历背包,再遍历物品
private static void testCompletePackAnotherWay(){
int[] weight = {1, 3, 4};
int[] value = {15, 20, 30};
int bagWeight = 4;
int[] dp = new int[bagWeight + 1];
for (int i = 1; i <= bagWeight; i++){ // 遍历背包容量
for (int j = 0; j < weight.length; j++){ // 遍历物品
if (i - weight[j] >= 0){
dp[i]+=dp[i-weight[j]];
}
}
}
for (int maxValue : dp){
System.out.println(maxValue + " ");
}
}
关于对排列的解释🦈
/*
背包容量为5,有三个物品,分别重量分别为1,2,5。
按照排列的方式有多少种方法可以装满背包?
*/
bagSize = 5, weight = [1, 2, 5]

我们可以看到,在遍历bagSize=2之前,bagSize=1已经被完全遍历完了,同理,在遍历bagSize=3之前,2也被遍历完了。所以当遍历物品1、bagSize=3时,我们会将(2,1)添加进来,当遍历物品2、bagSize=3时,我们会添加(1,2),即bagSize=2时的排列的(2,1)和bagSize=3时排列的(1,2)都添加进来了,所以得到的方案数量是“排列”。
2、外层物品,内层背包:组合🥘
代码的实现:
//先遍历物品,再遍历背包
private static void testCompletePack(){
int[] weight = {1, 3, 4};
int[] value = {15, 20, 30};
int bagWeight = 4;
int[] dp = new int[bagWeight + 1];
for (int i = 0; i < weight.length; i++){ // 遍历物品
// 遍历背包容量!!!这里可以直接让j初始值为weight[i]
for (int j = weight[i]; j <= bagWeight; j++){
dp[j]+=dp[j-weight[i]];
}
}
for (int maxValue : dp){
System.out.println(maxValue + " ");
}
}
}
关于对组合的解释🦈
/*
背包容量为5,有三个物品,分别重量分别为1,2,5。
按照组合的方式有多少种方法可以装满背包?
*/
bagSize = 5, weight = [1, 2, 5]

我们可以发现,当遍历bagSize=3、物品1时,bagSize=2、物品2并没有遍历,导致(2)还没有被添加进dp[2]中,进而dp[3]中也不会有(2)。同理,当遍历bagSize=4、物品1时,bagSize=3、物品2并没有遍历,导致了此时的dp[3]中并没有(1,2),dp[4]中自然也不会添加进来(后续不会再出现dp[4]+=dp[3]的情况,因为物品1已经遍历过了,dp[4]+=dp[4-1]的情况不会再出现)。这样子就导致了没有排列的情况,所以得到的结果是”组合“。

浙公网安备 33010602011771号