完全背包总结二

1.完全背包和0/1背包的区别?

完全背包的物体有无限个,可以多次放入

0/1背包的物体只有一个,只能放入一次

2.关于物品遍历顺序

在0/1背包中为了防止物品被重复放入,所以选择倒序遍历背包

而完全背包中,可以重复放入,所以选择正序遍历背包

具体来说,如题目

重量 价值
物品1 1 15
物品2 3 20
物品3 5 35

求大小为4的背包可获得的最大价值。

(1)对于0/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.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]没有物品的累积。

(2)对于完全背包来说:物品可以重复放入

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

可以观察到物品被重复放入

(3)对于0/1背包问题,双for循环顺序能否颠倒

尝试先背包后物品,是否会对结果有影响

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的遍历顺序是先背包后物品

保证了防止最合适的一个物品,由于左边的背包没有计算,所以无法累计物品的重量

最后只是选择了能放入了一个最合适的物品

所以这种方式是不可以的。

(4)对于完全背包问题,双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

所以,对于完全背包来说,只是更新的方式不一样,结果是一样的。

先物品后背包<=>行更新

先背包后物品<=>列更新

3.总结

0/1背包问题

        双for循环的的顺序不可以颠倒

        背包倒序遍历才能保证物品取用一次

完全背包问题

        双for循环顺序可以颠倒,只是更新是行更新还是列更新的问题

        背包正序,每个物品都可以取用无限次。

相关推荐

  1. 完全背包总结

    2024-02-11 18:08:01       29 阅读
  2. 算法日记-02完全背包和多重背包问题总结

    2024-02-11 18:08:01       24 阅读
  3. 01背包完全背包

    2024-02-11 18:08:01       21 阅读
  4. 完全背包详解--模板

    2024-02-11 18:08:01       39 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-02-11 18:08:01       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-11 18:08:01       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-11 18:08:01       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-11 18:08:01       18 阅读

热门阅读

  1. Elasticsearch中的模板:定义、作用与实践

    2024-02-11 18:08:01       32 阅读
  2. 计算机网络(第六版)复习提纲29

    2024-02-11 18:08:01       29 阅读
  3. 2023年股市总结,2024年A股方向展望!

    2024-02-11 18:08:01       34 阅读
  4. 前端开发_Node.js

    2024-02-11 18:08:01       25 阅读
  5. C语言什么是悬空指针?

    2024-02-11 18:08:01       35 阅读
  6. Leetcode 518 零钱兑换 II

    2024-02-11 18:08:01       27 阅读
  7. 线程池设计---C++

    2024-02-11 18:08:01       24 阅读
  8. JDK8常用:JVM参数

    2024-02-11 18:08:01       19 阅读
  9. STL之priority_queue的使用及其模拟实现+仿函数

    2024-02-11 18:08:01       26 阅读
  10. 深入浅出TCP/IP协议簇:理论与Python实践

    2024-02-11 18:08:01       18 阅读
  11. 详解C指针 (二)

    2024-02-11 18:08:01       26 阅读