动态规划背包问题(模版)

(待补全完整)

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]);
    }
}

背包问题方案数

求背包问题的方案

依赖背包问题

参考资料

《算法笔试》

《背包九讲》

原文地址:https://www.cnblogs.com/CryFace/p/13541075.html