赞
踩
完全背包的物体有无限个,可以多次放入
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<weight.length;i++){//遍历物品
- for(int j=bag;j>=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<arr.length;i++){
- System.out.print(arr[i]+"\t");
- }
- System.out.println();
- }
- }
拿起物品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<weight.length;i++){//遍历物品
- for(int j=1;j<=bag;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<arr.length;i++){
- System.out.print(arr[i]+"\t");
- }
- System.out.println();
- }
- }
拿起物品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<weight.length;i++){//遍历物品
- 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];
- }
拿起物品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<weight.length;i++){//遍历物品
- 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];
- }
- 拿起物品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循环顺序可以颠倒,只是更新是行更新还是列更新的问题
背包正序,每个物品都可以取用无限次。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。