zoukankan      html  css  js  c++  java
  • 动态规划背包问题(模版)

    (待补全完整)

    01背包

    问题描述

    有n件物品,每件物品的重量为w[i],价值为c[i]。现有一个容量为V的背包,问如何选取物品放入背包,使得背包内物品的价值最大。其中每种物品都有1件。

    样例输入

    5 8 // n == 5, V == 8

    3 5 1 2 2 //w[i] 重量

    4 5 2 1 3 //c[i] 价值

    结果为 10

    代码

    package 背包问题;
    
    import java.util.Scanner;
    
    public class 零一背包 {
        public static void main(String[] args) {
            Scanner in = new Scanner(System.in);
            int n = in.nextInt();
            int V = in.nextInt();
            int[] w = new int[n];
            int[] c = new int[n];
            int[] dp = new int[V+1];
            //输入一组物品的重量
            for(int i = 0; i < n; i++){
                w[i] = in.nextInt();
            }
            //输入一组物品的价值
            for(int i = 0; i < n; i++){
                c[i] = in.nextInt();
            }
            int maxn = 0;
            //核心过程
            for(int i = 0; i < n; i++){
                for(int v = V; v >= w[i]; v--){
                    dp[v] = Math.max(dp[v],dp[v - w[i]] + c[i]);
                    maxn = Math.max(dp[v],maxn);
                }
            }
            System.out.println(maxn);
        }
    }
    
    

    完全背包

    问题描述

    有n种物品,每种物品的单件重量为w[i],价值为c[i]。现在有一个容量为V的背包,问如何选取物品放入背包,使得背包内物品的总价值最大。其中每种物品都有无穷件。

    这里优化之后与01背包其实很是相似,原理不多讲。主要是记录一下模版

    代码

    package 背包问题;
    
    import java.util.Scanner;
    
    public class 完全背包 {
        public static void main(String[] args) {
            Scanner in = new Scanner(System.in);
            int n = in.nextInt();
            int V = in.nextInt();
            int[] w = new int[n];
            int[] c = new int[n];
            int[] dp = new int[V+1];
            //输入一组物品的重量
            for(int i = 0; i < n; i++){
                w[i] = in.nextInt();
            }
            //输入一组物品的价值
            for(int i = 0; i < n; i++){
                c[i] = in.nextInt();
            }
            int maxn = 0;
            for(int i = 0; i < n; i++){
                //与01背包相比多了逆序变成了正序的变化
                for(int v = w[i]; v <= V; v++){
                    dp[v] = Math.max(dp[v],dp[v - w[i]] + c[i]);
                    maxn = Math.max(dp[v],maxn);
                }
            }
            System.out.println(maxn);
        }
    }
    

    多重背包

    问题描述

    有n种物品,每种物品的重量为w[i],价值为c[i],数量为s[i]。现有一个容量为V的背包,问如何选取物品放入背包,使得背包内物品的价值最大。与01背包的差距就是多了数量的限制。

    这里呢,是最初始的模版,也就是直接三重for循环,通过o1背包加了一层循环来写的,直接添加了对数量的限制。

    代码

    package 背包问题;
    
    import java.util.Scanner;
    
    public class 多重背包 {
        public static void main(String[] args) {
            Scanner in = new Scanner(System.in);
            int n = in.nextInt();
            int V = in.nextInt();
            int[] w = new int[n];
            int[] c = new int[n];
            int[] s = new int[n];
            int[] dp = new int[V + 1];
            //输入一组物品的重量
            for (int i = 0; i < n; i++) {
                w[i] = in.nextInt();
            }
            //输入一组物品的价值
            for (int i = 0; i < n; i++) {
                c[i] = in.nextInt();
            }
            //输入一组物品的数量
            for (int i = 0; i < n; i++) {
                s[i] = in.nextInt();
            }
            //三重循环来进行背包填充
            for (int i = 0; i < n; i++) {
                for (int j = V; j >= w[i]; j--){
                    for(int k = 0;k <= s[i] && k * w[i] <= j; k++){
                        dp[j] = Math.max(dp[j],dp[j - k * w[i]] + k * c[i]);
                    }
                }
            }
            System.out.println(dp[V]);
        }
    }
    
    

    多重背包优化

    优化描述

    问题同上。上面我们虽然解决了多重背包的问题,但是问题也出现了,我们为了加一个限制,于是乎又加了一重循环,导致复杂度的增加,但是我们可以进行优化。

    具体思想就是将多重背包压缩成01背包,拆分s变成s组,可以将1-s的数量与价值进行绑定,相当于变成了01背包。但是这样做的拆分复杂度还是高,所以具体就是用到了二进制优化的思想。

    代码

    package 背包问题;
    
    import java.util.Scanner;
    
    public class 多重背包优化 {
        public static void main(String[] args) {
            Scanner in = new Scanner(System.in);
            int n = in.nextInt();
            int V = in.nextInt();
            int[] w = new int[n];
            int[] c = new int[n];
            int[] s = new int[n];
            int[] dp = new int[V + 1];
            //具体输入要求视题目而定可以优化代码
            //输入一组物品的重量
            for (int i = 0; i < n; i++) {
                w[i] = in.nextInt();
            }
            //输入一组物品的价值
            for (int i = 0; i < n; i++) {
                c[i] = in.nextInt();
            }
            //输入一组物品的数量
            for (int i = 0; i < n; i++) {
                s[i] = in.nextInt();
            }
            //我们需要对背包进行压缩
            int[] newW = new int[5000]; //压缩后的新重量,这里尽量开一个比较大的值,视题目要求
            int[] newC = new int[5000]; //压缩后的价值,这里尽量开一个比较大的值,视题目要求
            int cnt = 0; // 用来统计我们绑定后数组大小
            for(int i = 0; i < n; i++){
                for(int k = 1; k <= s[i]; k *= 2){
                    s[i] -= k;
                    newW[cnt] = w[i] * k;
                    newC[cnt] = c[i] * k;
                    cnt++;
                }
                //如果s[i]还有剩余继续添加
                if(s[i] > 0){
                    newW[cnt] = w[i] * s[i];
                    newC[cnt] = c[i] * s[i];
                    cnt++;
                }
            }
            //然后就是套用我们的01背包模版
            for(int i = 0; i < cnt; i++){
                for(int j = V; j >= newW[i]; j--){
                    dp[j] = Math.max(dp[j],dp[j - newW[i]] + newC[i]);
                }
            }
            System.out.println(dp[V]);
        }
    }
    
    

    分组背包

    题目描述

    有 NN 组物品和一个容量是 VV 的背包。每组物品有若干个,同一组内的物品最多只能选一个。每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。

    输出最大价值。

    这里我们改一下输入格式,我们以如下的输入格式进行代码的编写:

    代码

    package 背包问题;
    
    import java.util.Scanner;
    
    public class 分组背包 {
    
        static int N = 110;  //具体范围视题目要求
    
        public static void main(String[] args) {
            Scanner in = new Scanner(System.in);
            int n = in.nextInt();
            int V = in.nextInt();
            int[][] w = new int[N][N];
            int[][] c = new int[N][N];
            int[] s = new int[n];
            int[] dp = new int[N];
            for(int i = 0; i < n; i++){
                //每一组背包有多少的数量
                s[i] = in.nextInt();
                for(int j = 0; j < s[i]; j++){
                    w[i][j] = in.nextInt();
                    c[i][j] = in.nextInt();
                }
            }
    
            for(int i = 0; i < n; i++){
                for(int j = V; j >= 0; j--){
                    for(int k = 0; k < s[i]; k++){
                        if(j >= w[i][k]){  //如果我们的背包可以容纳当前组的可选背包的容量
                            dp[j] = Math.max(dp[j],dp[j - w[i][k]] + c[i][k]);
                        }
                    }
                }
            }
    
            System.out.println(dp[V]);
        }
    }
    
    
    

    混合背包

    题目描述

    解题思路

    我们可以进行一个判断,如果是-1进行01背包处理,如果是0进行完全背包处理,至于多重背包,上面也说过了,可以用二进制优化,也加入到01背包里面。这里我们可以用一个背包类来进行存储,然后实现代码,模版如下。

    代码

    package 背包问题;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Scanner;
    
    public class 混合背包 {
    
        static class Bag{
            int kind; //背包种类
            int w; //背包重量或者体积之类的限制
            int c; //背包价值
            Bag(int kind,int w,int c){
                this.kind = kind;
                this.w = w;
                this.c = c;
            }
        }
    
        static int N = 1010; //具体大小视题目而定
    
        public static void main(String[] args) {
            Scanner in = new Scanner(System.in);
            List<Bag> bags = new ArrayList<>();
            int n = in.nextInt();
            int V = in.nextInt();
            int[] dp = new int[N];
            for(int i = 0; i < n; i++){
                int w = in.nextInt();
                int c = in.nextInt();
                int s = in.nextInt();
                if(s < 1){
                    bags.add(new Bag(-1,w,c));
                }else if(s == 0){
                    bags.add(new Bag(0,w,c));
                }else{
                    //如果是多重背包则进行二进制优化
                    for(int j = 1; j <= s; j *= 2){
                        s -= j;
                        bags.add(new Bag(-1,w * j,c * j));
                    }
                    if(s > 0) bags.add(new Bag(-1,w * s,c * s));
                }
            }
            //然后就是分成了01背包和完全背包来处理
            for(Bag b : bags){
                if(b.kind == -1) {
                    for(int j = V; j >= b.w; j--){
                        dp[j] = Math.max(dp[j],dp[j - b.w] + b.c);
                    }
                }else{
                    for(int j = 0; j <= V; j++){
                        dp[j] = Math.max(dp[j],dp[j - b.w] + b.c);
                    }
                }
            }
            System.out.println(dp[V]);
        }
    }
    	
    

    二维费用背包

    二维费用背包,其实很好理解,就是原本我们背包只有一个重量的限制,现在可能多了一个体积的限制。

    相比一维的01背包其实就多了一重循环,同时我们也可以继续扩展问题,三维费用背包,四维费用背包等等。

    代码

    package 背包问题;
    
    import java.util.Scanner;
    
    public class 二维费用背包 {
    
        static int N = 1010; //视题目要求进行修改范围
    
        public static void main(String[] args) {
            Scanner in = new Scanner(System.in);
            int n = in.nextInt();
            int V = in.nextInt();
            int M = in.nextInt();
            int[][] dp = new int[N][N];
            for(int i = 0; i < n; i++){
                int v = in.nextInt(); //物品体积
                int m = in.nextInt(); //物品重量
                int c = in.nextInt(); //物品价值
                for(int j = V; j >= v; j--){
                    for(int k = M; k >= m; k--){
                        dp[j][k] = Math.max(dp[j][k],dp[j - v][k - m] + c);
                    }
                }
            }
            System.out.println(dp[V][M]);
        }
    }
    

    背包问题方案数

    求背包问题的方案

    依赖背包问题

    参考资料

    《算法笔试》

    《背包九讲》

  • 相关阅读:
    C语言文法分析
    词法分析--编程感想
    词法分析(改)
    0916词法分析
    0909关于编译
    python正则表达式
    python除法运算
    python值相等和对象相同
    Java编写程序时要考虑到所有可能的异常
    hadoop 读取文件操作
  • 原文地址:https://www.cnblogs.com/CryFace/p/13541075.html
Copyright © 2011-2022 走看看