当前位置: 首页 > news >正文

背包九讲

背包九讲

01背包

有 NN 件物品和一个容量是 VV 的背包。每件物品只能使用一次。

第 ii 件物品的体积是 vivi,价值是 wiwi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数,N,VN,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 NN 行,每行两个整数 vi,wivi,wi,用空格隔开,分别表示第 ii 件物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤10000<N,V≤1000
0<vi,wi≤10000<vi,wi≤1000

输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:

8

二维数组 

import java.util.*;

public class Main {
    //   f[i][j]  
    // 1.不选第i个物品 f[i][j]=f[i-1][j]
    // 2.选第i个物品  f[i][j]=f[i-1][j-v[i]]
    // f[0][0]=0;
    // f[i][j]=max(1,2);
    public static void main(String[] args) {
       Scanner in=new Scanner(System.in);
       int N=1010;
       int f[][]=new int[N][N];//前i个物品体积为j的时候的最大价值
       int v[]=new int[N];//体积
       int w[]=new int[N];//价值
       int n=in.nextInt();//数量
       int m=in.nextInt();//体积
       for(int i=1;i<=n;i++){
           v[i]=in.nextInt();
           w[i]=in.nextInt();
       }
       for(int i=1;i<=n;i++){
           for(int j=0;j<=m;j++){
               f[i][j]=f[i-1][j];//不选第i个物品
               if(j>=v[i])//选第i个物品
               f[i][j]=Math.max(f[i][j],f[i-1][j-v[i]]+w[i]);
           }
       }
       System.out.println(f[n][m]);
       
    }
}

 一维数组

import java.util.*;

public class Main {
    static final int N=1010;
    public static void main(String args[]){
     Scanner in=new Scanner(System.in);   
     int n=in.nextInt();
     int m=in.nextInt();
     int v[]=new int[N];
     int w[]=new int[N];
     int dp[]=new int[N];
     for(int i=1;i<=n;i++){
         v[i]=in.nextInt();
         w[i]=in.nextInt();
     }
     /*将二维数组压缩成一维数组不断更新
     dp[j]从最大容量开始更新,否则会将上一次循环的给更新掉
     j前边的部分就是二维数组前i-1个的结果
     所以从后边开始更新就不会影响结果
如果使用顺序,会先更新f[4],再更新f[7],对于这个书包问题来讲,
就是有可能,在更新f[4]的时候,已经把这次能加的物品加进来了,
然后更新f[7]的时候,还有可能再加一次,所以必须使用逆序,
保证,f[4]是没有加入新物品前,背包里的最优解。
     */
     for(int i=1;i<=n;i++){
         for(int j=m;j>=v[i];j--){
            //  if(j>=v[i])可以优化到循环内部
             dp[j]=Math.max(dp[j],dp[j-v[i]]+w[i]);
         }
     }
     System.out.println(dp[m]);
    }
}

初始化情况

f[0]  的初始值是0 其余都是负无穷(正好装满的时候最大值)

除0外初始化为负无穷,那么仅刚好装下的位置是正数(从0加),其余位置为负数(从负无穷加)

求恰好装满的最大价值,需要枚举一遍。

f[]  的初始值都是0(<=m时的最大值)

 完全背包问题

有 NN 种物品和一个容量是 VV 的背包,每种物品都有无限件可用。

第 ii 种物品的体积是 vivi,价值是 wiwi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数,N,VN,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 NN 行,每行两个整数 vi,wivi,wi,用空格隔开,分别表示第 ii 种物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤10000<N,V≤1000
0<vi,wi≤10000<vi,wi≤1000

输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
10

 完全背包和01背包的唯一区别就是一个物品可以用无限次

初始化跟01背包一样

import java.util.*;
public class Main{
    public static void main(String args[]){
        Scanner in=new Scanner(System.in);
        int N=1010;
        int n=in.nextInt();
        int m=in.nextInt();
        int dp[]=new int[N];
        int v[]=new int[N];
        int w[]=new int[N];
        for(int i=1;i<=n;i++){
            v[i]=in.nextInt();
            w[i]=in.nextInt();
        }
        for(int i=1;i<=n;i++){
            for(int j=v[i];j<=m;j++){
//从0开始因为每个物品可以重复放入
//可以省掉判断,放入循环中
                dp[j]=Math.max(dp[j],dp[j-v[i]]+w[i]);
            }
        }
        System.out.println(dp[m]);
    }
}

 多重背包问题 I

有 NN 种物品和一个容量是 VV 的背包。

第 ii 种物品最多有 sisi 件,每件体积是 vivi,价值是 wiwi。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式

第一行两个整数,N,VN,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 NN 行,每行三个整数 vi,wi,sivi,wi,si,用空格隔开,分别表示第 ii 种物品的体积、价值和数量。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤1000<N,V≤100
0<vi,wi,si≤1000<vi,wi,si≤100

输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10

 完全背包问题由于物品数量无限,在状态转移时,当考虑放入某个物品时,是基于当前物品已经考虑过的状态进行转移,与 0 - 1 背包和多重背包基于上一个物品的状态转移有所不同。

多重背包可以看作是01背包的延申        可以放入0,1,2,3······s

import java.util.*;

public class Main {
    public static void main(String[] args) {
       Scanner in=new Scanner(System.in);
      int N=200;
      int n=in.nextInt();
      int m=in.nextInt();
      int dp[]=new int[N];
      int v[]=new int[N];
      int w[]=new int[N];
      int s[]=new int[N];
      for(int i=1;i<=n;i++) {
    	  v[i]=in.nextInt();
    	  w[i]=in.nextInt();
    	  s[i]=in.nextInt();
      }
      for(int i=1;i<=n;i++) {
    	  for(int j=m;j>=v[i];j--) {
    		 for(int k=1;k<=s[i]&&k*v[i]<=j;k++)
    			  dp[j]=Math.max(dp[j], dp[j-k*v[i]]+k*w[i]);
    	  }
      }
      System.out.println(dp[m]);
    }
}

多重背包问题 II

有 NN 种物品和一个容量是 VV 的背包。

第 ii 种物品最多有 sisi 件,每件体积是 vivi,价值是 wiwi。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式

第一行两个整数,N,VN,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 NN 行,每行三个整数 vi,wi,sivi,wi,si,用空格隔开,分别表示第 ii 种物品的体积、价值和数量。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N≤10000<N≤1000
0<V≤20000<V≤2000
0<vi,wi,si≤20000<vi,wi,si≤2000

提示:

本题考查多重背包的二进制优化方法。

输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10

 

import java.util.Scanner;

public class Main {
    // 定义常量,N 用于存储分组后的物品数量,M 用于存储背包的最大容量
    static final int N = 12010, M = 2010;
    // n 表示物品的原始种类数,m 表示背包的容量
    static int n, m;
    // v 数组存储每个分组物品的体积,w 数组存储每个分组物品的价值
    static int[] v = new int[N];
    static int[] w = new int[N];
    // f 数组用于动态规划,存储不同容量下的最大价值
    static int[] f = new int[M];

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // 读取物品的原始种类数和背包的容量
        n = scanner.nextInt();
        m = scanner.nextInt();
        // cnt 用于记录分组后的物品组数
        int cnt = 0;
        // 遍历每一种原始物品
        for (int i = 1; i <= n; i++) {
            // a 表示单个物品的体积,b 表示单个物品的价值,s 表示该物品的数量
            int a = scanner.nextInt();
            int b = scanner.nextInt();
            int s = scanner.nextInt();
            // k 用于二进制拆分,从 1 开始
            int k = 1;
            // 进行二进制拆分
            while (k <= s) {
                cnt++;
                // 计算当前分组的体积
                v[cnt] = a * k;
                // 计算当前分组的价值
                w[cnt] = b * k;
                // 减去已经拆分出去的物品数量
                s -= k;
                // 二进制倍数增加
                k *= 2;
            }
            // 如果还有剩余物品,单独作为一组
            if (s > 0) {
                cnt++;
                v[cnt] = a * s;
                w[cnt] = b * s;
            }
        }
        // 将分组后的物品组数赋值给 n
        n = cnt;
        // 0 - 1 背包一维优化
        for (int i = 1; i <= n; i++) {
            for (int j = m; j >= v[i]; j--) {
                // 状态转移方程,更新最大价值
                f[j] = Math.max(f[j], f[j - v[i]] + w[i]);
            }
        }
        // 输出背包容量为 m 时的最大价值
        System.out.println(f[m]);
        scanner.close();
    }
}

混合背包问题

有 NN 种物品和一个容量是 VV 的背包。

物品一共有三类:

  • 第一类物品只能用1次(01背包);
  • 第二类物品可以用无限次(完全背包);
  • 第三类物品最多只能用 sisi 次(多重背包);

每种体积是 vivi,价值是 wiwi。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式

第一行两个整数,N,VN,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 NN 行,每行三个整数 vi,wi,sivi,wi,si,用空格隔开,分别表示第 ii 种物品的体积、价值和数量。

  • si=−1si=−1 表示第 ii 种物品只能用1次;
  • si=0si=0 表示第 ii 种物品可以用无限次;
  • si>0si>0 表示第 ii 种物品可以使用 sisi 次;
输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤10000<N,V≤1000
0<vi,wi≤10000<vi,wi≤1000
−1≤si≤1000−1≤si≤1000

输入样例
4 5
1 2 -1
2 4 1
3 4 0
4 5 2
输出样例:
8
import java.util.Scanner;

public class Main {
    // 定义数组的最大长度
    static final int MAX_LENGTH = 100010;
    // n 表示物品的种类数,m 表示背包的容量
    static int n, m;
    // v 数组存储物品的体积,w 数组存储物品的价值,f 数组用于动态规划
    static int[] v = new int[MAX_LENGTH];
    static int[] w = new int[MAX_LENGTH];
    static int[] f = new int[MAX_LENGTH];

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // 读取物品的种类数和背包的容量
        n = scanner.nextInt();
        m = scanner.nextInt();
        // cnt 用于记录二进制拆分后物品的总数
        int cnt = 1;

        // 遍历每种物品
        for (int i = 1; i <= n; i++) {
            // a 表示单个物品的体积,b 表示单个物品的价值,s 表示该物品的数量
            int a = scanner.nextInt();
            int b = scanner.nextInt();
            int s = scanner.nextInt();
            // 处理特殊情况:若 s 为负数,将其视为 1 个物品
            if (s < 0) {
                s = 1;
            } 
            // 若 s 为 0,将其视为完全背包问题,计算该物品最多能取的数量
            else if (s == 0) {
                s = m / a;
            }
            // 进行二进制拆分
            int k = 1;
            while (k <= s) {
                v[cnt] = a * k;
                w[cnt] = b * k;
                s -= k;
                k *= 2;
                cnt++;
            }
            // 处理剩余的物品
            if (s > 0) {
                v[cnt] = s * a;
                w[cnt] = s * b;
                cnt++;
            }
        }

        // 进行 0 - 1 背包的动态规划
        for (int i = 1; i < cnt; i++) {
            for (int j = m; j >= v[i]; j--) {
                f[j] = Math.max(f[j], f[j - v[i]] + w[i]);
            }
        }

        // 输出背包容量为 m 时能获得的最大价值
        System.out.println(f[m]);
        scanner.close();
    }
}

 二维费用的背包问题

有 NN 件物品和一个容量是 VV 的背包,背包能承受的最大重量是 MM。

每件物品只能用一次。体积是 vivi,重量是 mimi,价值是 wiwi。

求解将哪些物品装入背包,可使物品总体积不超过背包容量,总重量不超过背包可承受的最大重量,且价值总和最大。
输出最大价值。

输入格式

第一行三个整数,N,V,MN,V,M,用空格隔开,分别表示物品件数、背包容积和背包可承受的最大重量。

接下来有 NN 行,每行三个整数 vi,mi,wivi,mi,wi,用空格隔开,分别表示第 ii 件物品的体积、重量和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N≤10000<N≤1000
0<V,M≤1000<V,M≤100
0<vi,mi≤1000<vi,mi≤100
0<wi≤10000<wi≤1000

输入样例
4 5 6
1 2 3
2 4 4
3 4 5
4 5 6
输出样例:
8
import java.util.Scanner;

public class Main {
    // 定义常量 N,用于数组的大小
    static final int N = 1005;
    // n 表示物品的数量,V 表示背包的体积容量,M 表示背包的重量容量
    static int n, V, M;
    // v 数组存储每个物品的体积,m 数组存储每个物品的重量,w 数组存储每个物品的价值
    static int[] v = new int[N];
    static int[] m = new int[N];
    static int[] w = new int[N];
    // f 数组用于动态规划,f[j][k] 表示体积为 j 且重量为 k 时的最大价值
    static int[][] f = new int[N][N];

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // 读取物品数量、背包体积容量和背包重量容量
        n = scanner.nextInt();
        V = scanner.nextInt();
        M = scanner.nextInt();

        // 读取每个物品的体积、重量和价值
        for (int i = 1; i <= n; i++) {
            v[i] = scanner.nextInt();
            m[i] = scanner.nextInt();
            w[i] = scanner.nextInt();
        }

        // 进行动态规划,使用 0 - 1 背包的思路
        for (int i = 1; i <= n; i++) {
            for (int j = V; j >= v[i]; j--) {
                for (int k = M; k >= m[i]; k--) {
                    // 状态转移方程,更新最大价值
                    f[j][k] = Math.max(f[j - v[i]][k - m[i]] + w[i], f[j][k]);
                }
            }
        }

        // 输出体积为 V 且重量为 M 时的最大价值
        System.out.println(f[V][M]);
        scanner.close();
    }
}

 分组背包问题

有 NN 组物品和一个容量是 VV 的背包。

每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vijvij,价值是 wijwij,其中 ii 是组号,jj 是组内编号。

求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。

输出最大价值。

输入格式

第一行有两个整数 N,VN,V,用空格隔开,分别表示物品组数和背包容量。

接下来有 NN 组数据:

  • 每组数据第一行有一个整数 SiSi,表示第 ii 个物品组的物品数量;
  • 每组数据接下来有 SiSi 行,每行有两个整数 vij,wijvij,wij,用空格隔开,分别表示第 ii 个物品组的第 jj 个物品的体积和价值;
输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤1000<N,V≤100
0<Si≤1000<Si≤100
0<vij,wij≤1000<vij,wij≤100

输入样例
3 5
2
1 2
2 4
1
3 4
1
4 5
输出样例:
8

 

import java.util.Scanner;

public class Main {
    // 定义常量 N,作为数组的最大容量
    static final int N = 110;
    // f 数组用于存储状态,f[i][j] 表示只从前 i 组物品中选,当前体积小于等于 j 的最大值
    static int[][] f = new int[N][N];
    // v 数组存储物品体积,w 数组存储物品价值,s 数组存储每组物品的个数
    static int[][] v = new int[N][N];
    static int[][] w = new int[N][N];
    static int[] s = new int[N];
    // n 表示物品的组数,m 表示背包的容量
    static int n, m;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // 读取物品的组数和背包的容量
        n = scanner.nextInt();
        m = scanner.nextInt();

        // 读取每组物品的信息
        for (int i = 1; i <= n; i++) {
            s[i] = scanner.nextInt();
            for (int j = 0; j < s[i]; j++) {
                v[i][j] = scanner.nextInt();
                w[i][j] = scanner.nextInt();
            }
        }

        // 动态规划过程
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= m; j++) {
                // 不选当前组物品的情况
                f[i][j] = f[i - 1][j];
                // 枚举当前组的每个物品
                for (int k = 0; k < s[i]; k++) {
                    if (j >= v[i][k]) {
                        // 更新最大价值
                        f[i][j] = Math.max(f[i][j], f[i - 1][j - v[i][k]] + w[i][k]);
                    }
                }
            }
        }

        // 输出结果
        System.out.println(f[n][m]);
        scanner.close();
    }
}
import java.util.Scanner;

public class Main {
    // 定义常量 N,作为数组的最大容量
    static final int N = 110;
    // f 数组用于存储状态,f[j] 表示当前体积小于等于 j 的最大值
    static int[] f = new int[N];
    // v 数组存储物品体积,w 数组存储物品价值,s 数组存储每组物品的个数
    static int[][] v = new int[N][N];
    static int[][] w = new int[N][N];
    static int[] s = new int[N];
    // n 表示物品的组数,m 表示背包的容量
    static int n, m;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // 读取物品的组数和背包的容量
        n = scanner.nextInt();
        m = scanner.nextInt();

        // 读取每组物品的信息
        for (int i = 0; i < n; i++) {
            s[i] = scanner.nextInt();
            for (int j = 0; j < s[i]; j++) {
                v[i][j] = scanner.nextInt();
                w[i][j] = scanner.nextInt();
            }
        }

        // 动态规划过程
        for (int i = 0; i < n; i++) {
            for (int j = m; j >= 0; j--) {
                // 枚举当前组的每个物品
                for (int k = 0; k < s[i]; k++) {
                    if (j >= v[i][k]) {
                        // 更新最大价值
                        f[j] = Math.max(f[j], f[j - v[i][k]] + w[i][k]);
                    }
                }
            }
        }

        // 输出结果
        System.out.println(f[m]);
        scanner.close();
    }
}

相关文章:

  • 全自动数据强制备份程序,无视占用直接硬复制各种数据文件、文档、音视频、软件、数据库等的VSS卷拷贝批处理脚本程序,解放双手,一劳永逸
  • el-table一格两行;概率;find
  • 每日一题——搜索二维矩阵
  • 动态规划--斐波那契类型
  • Java基础系列:深入解析包装器类型与类型转换的奥秘与陷阱
  • C#类型转换基本概念
  • 数据结构链表的C++实现
  • 【网络编程】同步和异步、阻塞和非阻塞,I/O和网络I/O
  • 基于Matlab的人脸识别的二维PCA
  • 3.8[a]cv
  • MySQL 面试篇
  • 静态时序分析STA——2. 数字单元库-(1)
  • Uniapp 页面返回不刷新?两种方法防止 onShow 触发多次请求!
  • 网络通信Socket中多态HandleIO设计模式深度解析
  • Hive八股
  • 计算机毕业设计SpringBoot+Vue.js社区医疗综合服务平台(源码+文档+PPT+讲解)
  • 一周热点-文本生成中的扩散模型- Mercury Coder
  • 最小栈 _ _
  • set、LinkedHashSet和TreeSet的区别、Map接口常见方法、Collections 工具类使用
  • 03.08
  • 美食鉴赏国内网站/网推接单平台
  • 保定百度网站建设/站长工具seo查询软件
  • 建设银行网站能变更手机号吗/廊坊seo排名公司
  • 目前做哪个网站能致富/深圳整合营销
  • 如何搭建php视频网站/啥都能看的浏览器
  • 深圳市社会保险网站/武汉seo认可搜点网络