完全背包总结二

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=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]没有物品的累积。

(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

拿起物品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;ipublic 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

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

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

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

3.总结

0/1背包问题

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

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

完全背包问题

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

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

你可能感兴趣的:(刷题训练营,算法)