其他分享
首页 > 其他分享> > 完全背包问题

完全背包问题

作者:互联网

------

##完全背包问题简介:whale:

有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。**每件物品都有无限个(也就是可以放入背包多次)**,求解将哪些物品装入背包里物品价值总和最大。

**完全背包和01背包问题唯一不同的地方就是,每种物品有无限件**。

------

##问题特征:nerd_face:

与01背包和多重背包不同的是,完全背包问题中的**每个物品数量都是无限**的

------

##思路转换:laughing:

在01背包的滚动数组实现代码中,我们发现如果**将bagSize的遍历按顺序遍历**,而不是倒序,那么**前面的dp[0]会被添加多次**,这就是完全背包问题的解决方式

------

## 代码实现:helicopter:

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、测试一遍

```java
//先遍历物品,再遍历背包
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 + " ");
}
}
```

##拓展:关于完全背包求解决方法的 *数量* 问题:grimacing:

###问题特征:

求装满背包**有几种方法**?

### 解决方法:

这种问题我们不在将dp[j]的意义作为背包容量为j时可以放入的物品的最大价值,而是将其意义定义为**背包容量为j可以装满背包的方法的数量!!!**

值得注意的一点是,由于完全背包问题的遍历比较特殊,**当两层for循环颠倒时,分别表示排列、组合情况下方法的数量**,两者并不相同。

###关于排列组合的解释

####1、外层背包,内层物品:排列:shallow_pan_of_food:

#####代码的实现:

```java
//先遍历背包,再遍历物品
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 + " ");
}
}
```

#####关于对排列的解释:shark:

```java
/*
背包容量为5,有三个物品,分别重量分别为1,2,5。
按照排列的方式有多少种方法可以装满背包?
*/
bagSize = 5, weight = [1, 2, 5]
```

![image](https://www.icode9.com/i/l/?n=22&i=blog/2544602/202203/2544602-20220305110534954-1291008771.png)


我们可以看到,在遍历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、外层物品,内层背包:组合:shallow_pan_of_food:

#####代码的实现:

```java
//先遍历物品,再遍历背包
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 + " ");
}
}
}
```

#####关于对组合的解释:shark:

```java
/*
背包容量为5,有三个物品,分别重量分别为1,2,5。
按照组合的方式有多少种方法可以装满背包?
*/
bagSize = 5, weight = [1, 2, 5]
```
![image](https://www.icode9.com/i/l/?n=22&i=blog/2544602/202203/2544602-20220305110554792-2096771870.png)


我们可以发现,**当遍历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]的情况不会再出现**)。这样子就导致了没有排列的情况,所以得到的结果是**”组合“**。

标签:背包,weight,int,完全,问题,遍历,物品,dp
来源: https://www.cnblogs.com/MyJyang/p/15967418.html