zoukankan      html  css  js  c++  java
  • 三种背包问题(01背包、完全背包、多重背包)【动态规划】


    转载自 【动态规划】三种背包问题(01背包、完全背包、多重背包)

    01背包

    问题描述

    给定n个物体(它们的重量为:w1,w2,......,wn,价值为:v1,v2,......,vn) 和 一个承受重量为W的背包,
    问怎么选取这些物体,放在背包中(不超过背包的承重),让所取的子集达到最大价值。

    实现思路

    穷举法

    只要给出n个物体的所有组合(子集),分别各个子集的总价值,去掉那些总重量超过背包承重W的子集之后,
    对剩下的子集中找到总价值最大的那一个,就是我们想要的结果了。
    由于n各物体的有2n个子集,所以上面的做法的时间复杂度将是O(2n),除非n很小,否则时间性能消耗非常严重。

    01背包算法基本实现

    /**
     * @Author hwj
     * @Date 2020/8/16 22:09
     * @Desc: 给定n个物体(它们的重量为:w1,w2,......,wn,价值为:v1,v2,......,vn) 和 一个承受重量为W的背包,
     * 问怎么选取这些物体,放在背包中(不超过背包的承重),让所取的子集达到最大价值。
     **/
    public class package01 {
        public static void main(String[] args){
            //w[]:物品重量,v[]:物品价值,m:背包承重,n:物品个数,maxValue[][]:状态
            int[] w = {0, 10, 3, 4, 5}; //第一个数为0,是为了让输出时,i表示有i个物品
            int[] v = {0, 3, 4, 6, 7};
            int m = 10;
            int n = 4;
            int[][] maxValue = new int[5][16];
            // 01背包算法
            for (int i=1; i<=n; i++) { //第一个物体 是 第1行
                for (int j=0; j<=m; j++) {
                    // 在背包承重为 j 时,所能达到的最大价值
                    if (i > 0) {
                        // maxValue[i-1][j]:最大价值为 在承重为 j 的情况下,不放入第i物体的最大价值
                        maxValue[i][j] = maxValue[i-1][j];
                        // 当承重开始超过第i个,比较 不放入i物体 与 放入 i 物体相比
                        if (j >= w[i]) {
                            maxValue[i][j] = max(maxValue[i][j], maxValue[i-1][j-w[i]] + v[i]);
                            //注:maxValue[i][j]其实就是maxValue[i-1][j]   因为上面的赋值
                        }
                    } else {          //初始化,只考虑一个物体
                        if (j >= w[1]) {
                            maxValue[1][j] = v[1];
                        }
                    }
                }
            }
    
            System.out.println("4个物品在背包承重为10的情况下的组合的最大价值为:"+maxValue[n][m]);
            System.out.println();
    
            // 打印背包的不同承重量
            System.out.print("   " + "	");
            for (int i=0; i<=m; i++) {
                System.out.print(i + "	");
            }
            System.out.println();
    
            // 打印01背包算法 得到的状态矩阵值
            for (int i=1; i<=n; i++) {
                System.out.print("i="+ i +"	");
                for (int j=0; j<=m; j++) {
                    System.out.print(maxValue[i][j]+"	");
                }
                System.out.println();
            }
        }
    
        public static int max(int a, int b) {
            if (a > b) {
                return a;
            }
            return b;
        }
    }
    
    

    一维数组实现 01背包

    01背包还可以用一维数组实现,只不过此时的递推式 & 初始条件就需要做些改变了。
    要想用一维数组存放所有状态,也就是让该数组某个时间是第 i-1 层的状态,而过一段时间之后则成为第 i 层的状态。
    覆盖的过程中,应该采用从后到前的顺序遍历。首先,改写maxValue[ W ]的值。
    这是由于计算 i层 的maxValue[ W-1 ] 不需要用到 i-1 的maxValue[ W ]状态,所以,maxValue[ W ]的改动不影响maxValue[ W-1 ]的计算。
    以此类推,就可以在原来的数组上面不断覆盖最新一层的状态值了。
    
    /**
     * @Author hwj
     * @Date 2020/8/16 22:09
     * @Desc: 给定n个物体(它们的重量为:w1,w2,......,wn,价值为:v1,v2,......,vn) 和 一个承受重量为W的背包,
     * 问怎么选取这些物体,放在背包中(不超过背包的承重),让所取的子集达到最大价值。
     **/
    public class package01 {
        public static void main(String[] args){
            //w[]:物品重量,v[]:物品价值,m:背包承重,n:物品个数,maxValue[][]:状态
            int[] w = {0, 10, 3, 4, 5}; //第一个数为0,是为了让输出时,i表示有i个物品
            int[] v = {0, 3, 4, 6, 7};
            int m = 10;
            int n = 4;
            int[] maxValue = new int[16];
            // 01背包算法
            for (int i=1; i<=n; i++) {
                for (int j=m; j>=w[i]; j--) {
                    maxValue[j] = max(maxValue[j], maxValue[j-w[i]] + v[i]);
                }
    
                //验证  结果和二维实现的输出结果完全一样
                //for (int k=0; k<=m; k++) {
                //	System.out.print(maxValue[k] + "	");
                //}
                //System.out.println();
            }
    
    
            System.out.println("4个物品在背包承重为10的情况下的组合的最大价值为:"+maxValue[m]);
            System.out.println();
    
            // 打印背包的不同承重量
            System.out.print("   " + "	");
            for (int i=0; i<=m; i++) {
                System.out.print(i + "	");
            }
            System.out.println();
    
            // 打印01背包算法 得到的状态矩阵值
            for (int i=1; i<=n; i++) {
                System.out.print("i="+ i +"	");
                for (int j=0; j<=m; j++) {
                    System.out.print(maxValue[j]+"	");
                }
                System.out.println();
            }
        }
    
        public static int max(int a, int b) {
            if (a > b) {
                return a;
            }
            return b;
        }
    }
    
    

    完全背包

    问题描述

    完全背包是在01背包的基础上加了个条件——这n种物品都有无限的数量可以取,问怎样拿才可以实现价值最大化。

    问题分析

    虽然题意中每种有无限件,但这里有个隐藏条件:背包承重量的固定性导致每种最多只能取某个值,再多就放不下了,这个值就是W / wi。也就是说,
    对于第 i 种物品,它可以取0,1,2,......,W / wi(向下取整)件。而在01背包中,对于第 i 种物品,只能取0,1件。我们可以看到,
    01背包其实就是完全背包的一个特例。所以我们可以用类似01背包的思路写出完全背包的基本算法。
    

    具体实现代码

    二维数组实现

    /**
     * @Author hwj
     * @Date 2020/8/16 23:30
     * @Desc:
     **/
    public class PackageComplete {
        public static void main(String[] args) {
            int[] w = {0,10, 3, 4, 5};
            int[] v = {0,3, 4, 6, 7};
            int m = 10;
            int n = 4;
            int[][] maxValue = new int[5][16];
    
            for (int i=1; i<=n; i++) {
                for (int j=0; j<=m; j++) {
                    if (i > 1) {
                        maxValue[i][j] = maxValue[i-1][j];
                        //if (j >= v[i]) {
                        //	maxValue[i][j] = max(maxValue[i][j], maxValue[i-1][j-v[i]] + w[i]);
                        //}
                        if (j/w[i] >= 1) {
                            int maxTmp = 0;
                            // 对于i个物品,进行j/w[i]次比较得到最大值;而01背包中只需要进行1次比较
                            for (int k=1; k<=j/w[i]; k++) {
                                if (maxValue[i-1][j-k*w[i]] + k*v[i] > maxTmp) {
                                    maxTmp = maxValue[i-1][j-k*w[i]] + k*v[i];
                                }
                            }
                            maxValue[i][j] = max(maxValue[i][j], maxTmp);
                        }
                    } else {
                        //if (j >= v[0]) {
                        //	maxValue[0][j] = w[0];
                        //}
                        if (j/w[1] >= 1) {
                            maxValue[1][j] = j/w[1] * v[1];
                        }
                    }
                }
            }
    
            System.out.println("4个物品在背包承重为10的情况下的组合的最大价值为:"+maxValue[n][m]);
            System.out.println();
    
            // 打印背包的不同承重量
            System.out.print("   " + "	");
            for (int i=0; i<=m; i++) {
                System.out.print(i + "	");
            }
            System.out.println();
    
            // 打印01背包算法 得到的状态矩阵值
            for (int i=1; i<=n; i++) {
                System.out.print("i="+ i +"	");
                for (int j=0; j<=m; j++) {
                    System.out.print(maxValue[i][j]+"	");
                }
                System.out.println();
            }
        }
    
        public static int max(int a, int b) {
            if (a > b) {
                return a;
            }
            return b;
        }
    
    }
    
    

    一维数组实现

    /**
     * @Author hwj
     * @Date 2020/8/16 23:30
     * @Desc:
     **/
    public class PackageComplete {
    
        public static void main(String[] args) {
            int[] w = {0,10, 3, 4, 5};
            int[] v = {0,3, 4, 6, 7};
            int m = 10;
            int n = 4;
            int[] maxValue = new int[16];
    
            for (int i=1; i<=n; i++) {
                //for (int j=m; j>=w[i]; j--) {
                // 正序遍历; 01背包是逆序遍历
                for (int j=w[i]; j<=m; j++) {
                    maxValue[j] = max(maxValue[j], maxValue[j-w[i]] + v[i]);
                }
    
                //验证  结果和二维实现的输出结果完全一样
                //for (int k=0; k<=m; k++) {
                //	System.out.print(maxValue[k] + "	");
                //}
                //System.out.println();
            }
    
            System.out.println("4个物品在背包承重为10的情况下的组合的最大价值为:"+maxValue[m]);
            System.out.println();
    
            for (int i=0; i<=m; i++) {
                System.out.print(maxValue[i] + "	");
            }
        }
    
        public static int max(int a, int b) {
            if (a > b) {
                return a;
            }
            return b;
        }
    }
    
    

    多重背包

    问题描述

    多重背包是在01背包的基础上,加了个条件:第 i 件物品有ni件。

    问题分析

    我们考虑一下,如果所有ni都满足ni ≥ W / wi,那不就变成完全背包的问题了么。可见,完全背包的基本实现思路也可以应用到多重背包的基本实现。对于多重背包的基本实现,与完全背包是基本一样的,不同就在于物品的个数上界不再是v/c[i]而是n[i]与v/c[i]中较小的那个。所以我们要在完全背包的基本实现之上,再考虑这个上界问题。

    代码实现如下所示,代码与完全背包的区别除了多了个表示物品个数的数组n[ ]之外,只在内循环的控制条件那里。

    ## 二维数组实现
    /**
     * @Author hwj
     * @Date 2020/8/16 23:40
     * @Desc:
     **/
    public class MultiplePackage {
        public static void main(String[] args) {
            int[] w = {0,10, 3, 4, 5};
            int[] v = {0,3, 4, 6, 7};
            //第i个物品对应的个数
            int[] mount = {0,5,1,2,1};
            int m = 10;
            int n = 4;
            int[][] maxValue = new int[5][16];
    
            for (int i=1; i<=n; i++) {
                for (int j=0; j<=m; j++) {
                    if (i > 1) {
                        maxValue[i][j] = maxValue[i-1][j];
                        if (j/w[i] >= 1) {
                            int maxTmp = 0;
                            //for (int k=1; k<=j/w[i]; k++) {
                            //多重背包与完全背包的区别只在内循环这里
                            for (int k=1; k<=j/w[i] && k<=mount[i]; k++) {
                                if (maxValue[i-1][j-k*w[i]] + k*v[i] > maxTmp) {
                                    maxTmp = maxValue[i-1][j-k*w[i]] + k*v[i];
                                }
                            }
                            maxValue[i][j] = max(maxValue[i][j], maxTmp);
                        }
                    } else {
                        if (j/w[1] >= 1) {
                            maxValue[1][j] = j/w[1] * v[1];
                        }
                    }
                }
            }
    
            System.out.println("4个物品在背包承重为10的情况下的组合的最大价值为:"+maxValue[n][m]);
            System.out.println();
    
            // 打印背包的不同承重量
            System.out.print("   " + "	");
            for (int i=0; i<=m; i++) {
                System.out.print(i + "	");
            }
            System.out.println();
    
            // 打印01背包算法 得到的状态矩阵值
            for (int i=1; i<=n; i++) {
                System.out.print("i="+ i +"	");
                for (int j=0; j<=m; j++) {
                    System.out.print(maxValue[i][j]+"	");
                }
                System.out.println();
            }
        }
    
        public static int max(int a, int b) {
            if (a > b) {
                return a;
            }
            return b;
        }
    
    }
    
    
  • 相关阅读:
    linux下mysql数据的导出和导入
    mysql 命令行导数据库
    前端开发中经常使用到的20个正则表达式
    HTML5学习内容总结
    HTML5表单type类型详解
    表单说明(部分)
    HTML5和HTML4的区别(常用部分)
    HTML基础知识
    认识表单元素
    学习html5第一天
  • 原文地址:https://www.cnblogs.com/alidata/p/13514584.html
Copyright © 2011-2022 走看看