完全背包的物体有无限个,可以多次放入
0/1背包的物体只有一个,只能放入一次
在0/1背包中为了防止物品被重复放入,所以选择倒序遍历背包
而完全背包中,可以重复放入,所以选择正序遍历背包
具体来说,如题目
重量 价值 物品1 1 15 物品2 3 20 物品3 5 35 求大小为4的背包可获得的最大价值。
package other;
import java.util.Arrays;
import java.util.Objects;
public class Bag {
public static void main(String[] args) {
int[] weight=new int[]{1,3,4};
int[] values=new int[]{15,20,30};
Bag obj=new Bag();
System.out.println("result: "+ obj.simpleBag2(weight,values,4));
}
public int simpleBag2(int[] weight,int[] values,int bag){
int dp[]=new int[bag+1];
//初始化
Arrays.fill(dp,0);
//双层for循环
for(int i=0;i=0;j--){//遍历背包
System.out.println("拿起物品i="+i+" ,\t重量:"+weight[i]+"\t背包容量:"+j);
if(weight[i]<=j){
dp[j]=Math.max(dp[j],dp[j-weight[i]]+values[i]);
System.out.println("放入物品i="+i+" ,\t价值:"+values[i]);
} else {
dp[j]=dp[j];
System.out.println("不放入物品i="+i);
}
printArr(j,dp);
}
}
printArr(null,dp);
return dp[dp.length-1];
}
public void printArr(Integer j,int []arr){
if(!Objects.isNull(j)){
System.out.print("j = "+j+" :\t\t");
}
for(int i=0;i
拿起物品i=0 , 重量:1 背包容量:4
放入物品i=0 , 价值:15
j = 4 : 0 0 0 0 15
拿起物品i=0 , 重量:1 背包容量:3
放入物品i=0 , 价值:15
j = 3 : 0 0 0 15 15
拿起物品i=0 , 重量:1 背包容量:2
放入物品i=0 , 价值:15
j = 2 : 0 0 15 15 15
拿起物品i=0 , 重量:1 背包容量:1
放入物品i=0 , 价值:15
j = 1 : 0 15 15 15 15
拿起物品i=0 , 重量:1 背包容量:0
不放入物品i=0
j = 0 : 0 15 15 15 15
拿起物品i=1 , 重量:3 背包容量:4
放入物品i=1 , 价值:20
j = 4 : 0 15 15 15 35
拿起物品i=1 , 重量:3 背包容量:3
放入物品i=1 , 价值:20
j = 3 : 0 15 15 20 35
拿起物品i=1 , 重量:3 背包容量:2
不放入物品i=1
j = 2 : 0 15 15 20 35
拿起物品i=1 , 重量:3 背包容量:1
不放入物品i=1
j = 1 : 0 15 15 20 35
拿起物品i=1 , 重量:3 背包容量:0
不放入物品i=1
j = 0 : 0 15 15 20 35
拿起物品i=2 , 重量:4 背包容量:4
放入物品i=2 , 价值:30
j = 4 : 0 15 15 20 35
拿起物品i=2 , 重量:4 背包容量:3
不放入物品i=2
j = 3 : 0 15 15 20 35
拿起物品i=2 , 重量:4 背包容量:2
不放入物品i=2
j = 2 : 0 15 15 20 35
拿起物品i=2 , 重量:4 背包容量:1
不放入物品i=2
j = 1 : 0 15 15 20 35
拿起物品i=2 , 重量:4 背包容量:0
不放入物品i=2
j = 0 : 0 15 15 20 35
0 15 15 20 35
result: 35
可以观察到,由于dp[i]总是由前面的dp[i-1]推导来的,但是每次更新时,dp[i-1]没有物品的累积。
package other;
import java.util.Arrays;
import java.util.Objects;
public class Bag {
public static void main(String[] args) {
int[] weight=new int[]{1,3,4};
int[] values=new int[]{15,20,30};
Bag obj=new Bag();
System.out.println("result: "+ obj.simpleBag(weight,values,4));
}
public int simpleBag(int[] weight,int[] values,int bag){
int dp[]=new int[bag+1];
//初始化
Arrays.fill(dp,0);
//双层for循环
for(int i=0;i
拿起物品i=0 , 重量:1 背包容量:1
放入物品i=0 , 价值:15
j = 1 : 0 15 0 0 0
拿起物品i=0 , 重量:1 背包容量:2
放入物品i=0 , 价值:15
j = 2 : 0 15 30 0 0
拿起物品i=0 , 重量:1 背包容量:3
放入物品i=0 , 价值:15
j = 3 : 0 15 30 45 0
拿起物品i=0 , 重量:1 背包容量:4
放入物品i=0 , 价值:15
j = 4 : 0 15 30 45 60
拿起物品i=1 , 重量:3 背包容量:1
不放入物品i=1
j = 1 : 0 15 30 45 60
拿起物品i=1 , 重量:3 背包容量:2
不放入物品i=1
j = 2 : 0 15 30 45 60
拿起物品i=1 , 重量:3 背包容量:3
放入物品i=1 , 价值:20
j = 3 : 0 15 30 45 60
拿起物品i=1 , 重量:3 背包容量:4
放入物品i=1 , 价值:20
j = 4 : 0 15 30 45 60
拿起物品i=2 , 重量:4 背包容量:1
不放入物品i=2
j = 1 : 0 15 30 45 60
拿起物品i=2 , 重量:4 背包容量:2
不放入物品i=2
j = 2 : 0 15 30 45 60
拿起物品i=2 , 重量:4 背包容量:3
不放入物品i=2
j = 3 : 0 15 30 45 60
拿起物品i=2 , 重量:4 背包容量:4
放入物品i=2 , 价值:30
j = 4 : 0 15 30 45 60
0 15 30 45 60
result: 60
可以观察到物品被重复放入
尝试先背包后物品,是否会对结果有影响
public int simpleBag3(int[] weight,int[] values,int bag){
int dp[]=new int[bag+1];
//初始化
Arrays.fill(dp,0);
//双层for循环
for(int j=bag;j>=0;j--){//遍历背包
for(int i=0;i
拿起物品i=0 , 重量:1 背包容量:4
放入物品i=0 , 价值:15
j = 4 : 0 0 0 0 15
拿起物品i=1 , 重量:3 背包容量:4
放入物品i=1 , 价值:20
j = 4 : 0 0 0 0 20
拿起物品i=2 , 重量:4 背包容量:4
放入物品i=2 , 价值:30
j = 4 : 0 0 0 0 30
拿起物品i=0 , 重量:1 背包容量:3
放入物品i=0 , 价值:15
j = 3 : 0 0 0 15 30
拿起物品i=1 , 重量:3 背包容量:3
放入物品i=1 , 价值:20
j = 3 : 0 0 0 20 30
拿起物品i=2 , 重量:4 背包容量:3
不放入物品i=2
j = 3 : 0 0 0 20 30
拿起物品i=0 , 重量:1 背包容量:2
放入物品i=0 , 价值:15
j = 2 : 0 0 15 20 30
拿起物品i=1 , 重量:3 背包容量:2
不放入物品i=1
j = 2 : 0 0 15 20 30
拿起物品i=2 , 重量:4 背包容量:2
不放入物品i=2
j = 2 : 0 0 15 20 30
拿起物品i=0 , 重量:1 背包容量:1
放入物品i=0 , 价值:15
j = 1 : 0 15 15 20 30
拿起物品i=1 , 重量:3 背包容量:1
不放入物品i=1
j = 1 : 0 15 15 20 30
拿起物品i=2 , 重量:4 背包容量:1
不放入物品i=2
j = 1 : 0 15 15 20 30
拿起物品i=0 , 重量:1 背包容量:0
不放入物品i=0
j = 0 : 0 15 15 20 30
拿起物品i=1 , 重量:3 背包容量:0
不放入物品i=1
j = 0 : 0 15 15 20 30
拿起物品i=2 , 重量:4 背包容量:0
不放入物品i=2
j = 0 : 0 15 15 20 30
0 15 15 20 30
result: 30
为了保证物品只用一次,所以背包的遍历一定是倒序遍历,颠倒双for的遍历顺序是先背包后物品
保证了防止最合适的一个物品,由于左边的背包没有计算,所以无法累计物品的重量
最后只是选择了能放入了一个最合适的物品
所以这种方式是不可以的。
尝试先背包后物品,是否会对结果有影响
public int simpleBag4(int[] weight,int[] values,int bag){
int dp[]=new int[bag+1];
//初始化
Arrays.fill(dp,0);
//双层for循环
for(int j=1;j<=bag;j++){//遍历背包
for(int i=0;i
拿起物品i=0 , 重量:1 背包容量:1
放入物品i=0 , 价值:15
j = 1 : 0 15 0 0 0
拿起物品i=1 , 重量:3 背包容量:1
不放入物品i=1
j = 1 : 0 15 0 0 0
拿起物品i=2 , 重量:4 背包容量:1
不放入物品i=2
j = 1 : 0 15 0 0 0
拿起物品i=0 , 重量:1 背包容量:2
放入物品i=0 , 价值:15
j = 2 : 0 15 30 0 0
拿起物品i=1 , 重量:3 背包容量:2
不放入物品i=1
j = 2 : 0 15 30 0 0
拿起物品i=2 , 重量:4 背包容量:2
不放入物品i=2
j = 2 : 0 15 30 0 0
拿起物品i=0 , 重量:1 背包容量:3
放入物品i=0 , 价值:15
j = 3 : 0 15 30 45 0
拿起物品i=1 , 重量:3 背包容量:3
放入物品i=1 , 价值:20
j = 3 : 0 15 30 45 0
拿起物品i=2 , 重量:4 背包容量:3
不放入物品i=2
j = 3 : 0 15 30 45 0
拿起物品i=0 , 重量:1 背包容量:4
放入物品i=0 , 价值:15
j = 4 : 0 15 30 45 60
拿起物品i=1 , 重量:3 背包容量:4
放入物品i=1 , 价值:20
j = 4 : 0 15 30 45 60
拿起物品i=2 , 重量:4 背包容量:4
放入物品i=2 , 价值:30
j = 4 : 0 15 30 45 60
0 15 30 45 60
result: 60
Process finished with exit code 0
所以,对于完全背包来说,只是更新的方式不一样,结果是一样的。
先物品后背包<=>行更新
先背包后物品<=>列更新
0/1背包问题:
双for循环的的顺序不可以颠倒
背包倒序遍历才能保证物品取用一次
完全背包问题:
双for循环顺序可以颠倒,只是更新是行更新还是列更新的问题
背包正序,每个物品都可以取用无限次。