说说几种背包问题(java实现)

背包问题是经典的动态规划问题。分成以下几种细说一下背包问题。代码为java实现。

1. 01背包问题

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。

定义一个二阶矩阵dp[N+1][V+1],
dp[i][j]表示在 只能选择前i个物品,背包容量为j的情况下,背包中物品的最大价值
这里之所以要N+1和V+1,是因为第0行表示只能选择第0个物品的时候,即没有物品的时候第0列表示背包的体积为0的时候,即不能装任何东西的时候.

已知dp[i-1][j],要求dp[i][j],则需要状态转移方程
对于dp[i][j]有两种情况:
  1.不选择当前的第i件物品/第i件物品比背包容量要大
            dp[i][j] = dp[i-1][j]
  2.选择当前的第i件物品(潜在要求第i件物品体积小于等于背包总容量),则能装入的物品最大价值为:当前物品的价值 加上 背包剩余容量在只能选前i-1件物品的情况下的最大价值。表示为:
          dp[i][j] = dp[i-1][j-v[i]] + w[i]
dp[i][j]在两种情况中选择比较大的情况作为当前的最优解;
即:
   if(j >= v[i]):
      dp[i][j] = max(dp[i-1][j], dp[i-1][j-v[i]] + w[i])
   else:
      dp[i][j] = dp[i-1][j]

以上的动态规划需要一个二维的dp空间。我们可以优化为一个一维的dp数组。此时,dp[v]就表示在v大小的背包空间下,可以放入的最大的物品价值 。此时的物品依次放入。当我们已知第i-1个物品放入后,v的最大值,那么加入第i个物品时,最大值要么是放入第i个物品,要么是原来的值。表达式:
      dp[v] = Math.max( dp[v - v[i] ) + w[i] , dp[v])
参考代码如下:

import java.util.Scanner;public class Main{public static void main(String[] args) throws Exception {// 读入数据的代码Scanner reader = new Scanner(System.in);// 物品的数量为Nint N = reader.nextInt();// 背包的容量为Vint V = reader.nextInt();// 一个长度为N的数组,第i个元素表示第i个物品的体积;int[] v = new int[N + 1] ;// 一个长度为N的数组,第i个元素表示第i个物品的价值;int[] w = new int[N + 1] ;for (int i=1 ; i <= N ; i++){//读入是第一个数索引为1// 接下来有 N 行,每行有两个整数:v[i],w[i],用空格隔开,分别表示第i件物品的体积和价值v[i] = reader.nextInt();w[i] = reader.nextInt();}reader.close() ;// 二维dpint[][] dp = new int[N+1][V+1];dp[0][0] = 0;//此处i从1开始是因为上面v[i]和w[i]是从1开始存的for(int i = 1; i <= N; i++){for(int j = 0; j <= V; j++){if(j >= v[i]){dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-v[i]] + w[i]);}else{dp[i][j] = dp[i-1][j];}}}//压缩为一维dp数组int[] dp = new int[V+1];dp[0] = 0;for(int i = 1; i <= N; i++){for(int j = V; j >= v[i]; j--){dp[j] = Math.max(dp[j], dp[j-v[i]] + w[i]);}}

2.完全背包问题

有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用
第 i 种物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。
可以模仿01背包,dp[i][j]记录前i个物品放入j空间的最大值。此时,由于一个物品可是使用无限次数,dp[i][j]应该从dp[i][j-v[i]]来而不是dp[i-1][j-v[i]]来。表达式为

dp[i][j]=Math.max(dp[i][j-v[i]]+w[i],dp[i-1][j]) 同样,完全背包问题一样可以优化至一维dp空间。 01背包和完全背包使最常用的背包问题。很多其他背包问题可以转换为这两个问题来思考。 参考代码如下:

//数据读取和上一题01背包相同//模仿01背包,多维的dp。dp[i][j]记录前i个物品放入j空间的最大值。int[][] dp=new int[N+1][V+1];for(int i=1;i<=N;i++){for(int j=0;j<=V;j++){if(j>=v[i]){//此时的状态方程,比较的大小//注意i-1和i的状态区别dp[i][j]=Math.max(dp[i][j-v[i]]+w[i],dp[i-1][j]);}else{dp[i][j]=dp[i-1][j];}}}System.out.println(dp[N][V]);//初步优化为一维数组//dp[v]表示在v空间下装入的最大价值int[] dp=new int[V+1];for(int i=1;i<=N;i++){for(int j = V; j >= v[i]; j --){//第i个物品可以使用0-k个,和不使用第i个物品的最大值比较,找到较大的一个//逐一找最大值,增加循环,用变量k遍历计算最大值。for(int k = 0; j-k* v[i] >= 0; k ++){dp[j] = Math.max(dp[j] , dp[j - k * v[i]] + k * w[i]);}}}System.out.println(dp[V]);  //再优化。int[] dp=new int[V+1];for(int i=1;i<=N;i++){//和01背包相比在于01背包从后向前遍历,由于使用到之前的状态,从后向前时前面的状态为0,确保了一个物品只使用了一次。//完全背包使用从前向后遍历,前面的状态先遍历。此时后面的状态再计算时,使第i个物品重复使用。for(int j =  v[i]; j <= V;  j ++){dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);}}System.out.println(dp[V]);

3 多重背包问题

有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
思路:
  参考完全背包问题,就是将完全背包中的数量限制改为si. (体现在for循环中)
优化:
  利用二进制,将背包数量分解,变为01背包问题。(他人处学习来的思路和代码)。
参考代码:

        //直接做法。int[] dp = new int[V+1];for(int i = 1; i <= N; i ++){for(int j = V; j >= v[i]; j --){//此处加入一个限制条件k<=s[i]即可//k可以从1开始,因为0就是dp[j]for(int k = 1; j - k * v[i] >= 0 && k <= s[i]; k ++ ){dp[j] = Math.max(dp[j], dp[j - k * v[i]] + k * w[i]);}}}System.out.println(dp[V]);       //参考地址https://www.acwing.com/solution/content/7495///优化的多重背包问题。        //将数量s分解。例如有一个体积为v,值为w,数量为7的。则分解为[v,w],[2v,2w],[4v,4w]三个物体            //将所有物体数量都分解。则化解为了01背包问题。       int maxN = 200002;int[] v = new int[maxN];int[] w = new int[maxN];Scanner jin = new Scanner (System.in);void run(){int n = jin.nextInt();int m = jin.nextInt();int p = 1;for (int i = 1; i <= n ; i++){int V = jin.nextInt();int W = jin.nextInt();int S = jin.nextInt();int k = 1;while (S > k){v[p] = V*k;w[p] = W*k;S -= k;k *= 2;p++;}if (S > 0){v[p] = V*S;w[p] = W*S;p ++;}}//到此为止,原来所有的物体分解为共p个独立的物体,每个限制使用1次,化解为01背包问题。       //共有p个物体,放入总体积m的背包。体积为v,价值为wint res = dp(p, m);System.out.println(res);}//同01背包问题。int dp(int n, int m){int[] f = new int[maxN];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]);}}return f[m];}public static void main(String[] args) {new solution().run();}//利用优先队列优化多重背包问题。看到过大佬用过这个思路。//(待学习)

4.混合背包问题

有 N 种物品和一个容量是 V 的背包。
物品一共有三类:
第一类物品只能用1次(01背包);
第二类物品可以用无限次(完全背包);
第三类物品最多只能用 si 次(多重背包);
每种体积是 vi,价值是 wi。
si=−1 表示第 i 种物品只能用1次;
si=0 表示第 i 种物品可以用无限次;
si>0 表示第 i 种物品可以使用 si 次;
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。

思路:
分类讨论。01背包可以看作只使用一次的多重背包问题。
参考代码:

        public static void main(String[] args){Scanner sc = new Scanner(System.in);int N = sc.nextInt(); // 物品个数int V = sc.nextInt(); // 背包总容量int[] dp = new int[V + 1];for(int i = 0; i < N; i++){int v = sc.nextInt(); // 体积int w = sc.nextInt(); // 价值int s = sc.nextInt(); // 数量if(s == 0){// 完全背包问题for(int j = v; j <= V; j++){dp[j] = Math.max(dp[j], dp[j - v] + w);}}else{// 多重背包问题,01背包是多重背包的特例,可以一并处理s = Math.abs(s);for(int j = 1; s >= j; s -= j, j *= 2){for(int k = V; k >= j * v; k--){dp[k] = Math.max(dp[k], dp[k - j * v] + j * w);}}if(s > 0){for(int j = V; j >= s * v; j--){dp[j] = Math.max(dp[j], dp[j - s * v] + s * w);}}}}System.out.println(dp[V]);}

5.二维背包问题

有 N 件物品和一个容量是 V 的背包,背包能承受的最大重量是 M。
每件物品只能用一次。体积是 vi,重量是 mi,价值是 wi。
初始的01背包问题多加入了一个限制
求解将哪些物品装入背包,可使物品总体积不超过背包容量,总重量不超过背包可承受的最大重量,且价值总和最大。
输出最大价值。
思路: 与01背包一样,二维dp扩充为三维dp

        public int two_dimension_knapsack_problem_1(int N, int V, int M, int[] v, int[] m, int[] w){int[][][] dp = new int[N+1][V+1][M+1];for(int i = 1; i <= N; i++){for(int j = 1; j <= V; j++){for(int k = 1; k <= M; k++){if(j < v[i] || k < m[i]){// 客观条件限制,不能选择当前物品Ndp[i][j][k] = dp[i-1][j][k];}else {//基本相同,多个限制dp[i][j][k] = Math.max(dp[i-1][j][k], dp[i-1][j-v[i]][k-m[i]] + w[i]);}}}}return dp[N][V][M];

注:许多代码与思路来源于anwin网站与相关题目题解。

几种背包问题(java实现)相关推荐

  1. java完全背包,一次性解决三种背包问题

    前言 首先,大概讲一下什么是"背包"问题:背包问题是指你有一个容量为V的背包,然后有n个物品在你面前,你要怎么装才能使得背包里的物品总价值最大.而每种物品是只有1个,还是有多个,亦 ...

  2. cmd 将文件夹下文件剪切到另外一个文件_总结java中文件拷贝剪切的5种方式-JAVA IO基础总结第五篇...

    本文是Java IO总结系列篇的第5篇,前篇的访问地址如下: 总结java中创建并写文件的5种方式-JAVA IO基础总结第一篇 总结java从文件中读取数据的6种方法-JAVA IO基础总结第二篇 ...

  3. java调试生命周期,一种基于JAVA的智能合约生命周期的管理方法与流程

    本发明涉及区块链技术,尤其涉及一种基于JAVA的智能合约生命周期的管理方法. 背景技术: 区块链技术,区块链是一种新型去中心化协议,能安全地存储数字货币交易或其他数据,信息不可伪造和篡改,区块链上的交 ...

  4. java 对象创建过程_5种创建Java对象的方式

    在本篇文章中,将介绍5种创建Java对象的方式.类是创建对象的基本模板,接下来将介绍5种不同的方式,利用Java类来实例化Java对象. 1. 使用new关键字 ​ 采用new关键字实例化对象是Jav ...

  5. 计算 java_两种计算Java对象大小的方法(转)

    原文:http://blog.csdn.net/iter_zc/article/details/41822719 另一篇类似文章:http://www.cnblogs.com/magialmoon/p ...

  6. 聊聊JVM(三)两种计算Java对象大小的方法

    普通对象的结构如下,按64位机器的长度计算 1. 对象头(_mark), 8个字节 2. Oop指针,如果是32G内存以下的,默认开启对象指针压缩,4个字节 3. 数据区 4.Padding(内存对齐 ...

  7. javaone_JavaOne 2012:101种改进Java的方法-开发人员参与为何如此重要

    javaone Bruno Souza , Martijn Verburg和Heather Vancura在希尔顿酒店的美国大陆宴会厅4中展示了" 101种改善Java的方法:开发人员为何如 ...

  8. JavaOne 2012:101种改进Java的方法-开发人员参与为何如此重要

    Bruno Souza , Martijn Verburg和Heather Vancura在希尔顿酒店的大陆宴会厅4中展示了" 101种改进Java的方法:开发人员参与为何如此重要" ...

  9. java进程调度怎么画图,[Java教程]进程调度的两种算法JAVA实现

    [Java教程]进程调度的两种算法JAVA实现 0 2015-10-21 12:00:08 (SJF分为preemptive shortest job first(抢占式)和non-preemptiv ...

最新文章

  1. [NOIP模拟测试9]题(Problem) 题解 (组合数全家桶+dp)
  2. Centos 下 Nginx 信号控制
  3. 统计学习导论:基于R应用——第二章习题
  4. iis 7 php_Windows server 2008 下基于IIS7配置php7.2运行环境
  5. 卡尔蔡司携手神策数据,赋能近视防控数字化
  6. 判断一个字符串是否是由另2个字符串交错组成的
  7. 叮!您有一份来自平安人寿的真AI情书
  8. Abp vNext 切换MySql数据库
  9. 计算机毕业设计中ASP.NET数据源控件
  10. 《单细胞生物》教学反思
  11. 使用burp对Tomcat 弱密码爆破
  12. linux cat命令追加,linux cat命令
  13. **汉服有哪些基本形制呢**
  14. banq修复_慧荣SM3271AB U盘量产加密及修好图文详细教程
  15. php导出excel无边框线,phpexcel设置边框不全或者只有竖线问题解决方法
  16. 联想电脑为什么没有计算机,联想笔记本电脑没有声音怎么办
  17. 微信里的小程序怎么制作
  18. java设置excel单元格文本右对齐,POI操作Excel--设置单元格对齐方式--day03
  19. 使用html+css+js实现一个静态页面(含源码)
  20. 2020 ccpc 吉林省赛 H

热门文章

  1. VT-x/AMD-V 硬件加速在您的系统中不可用。您的 64-位虚拟机将无法检测到 64-位处理器,从而无法启动。
  2. 如何选择合适的渗压计?
  3. 新三板上市和主板上市的区别主要是什么?
  4. 金蝶eas系统服务器怎么启动,EAS应用服务器启动失败
  5. s5pv210 i2c 时序
  6. 爱因斯坦的题目:在你面前有一条长长的阶梯,如果每步跨2阶,那么最后剩1阶;如果每步跨3阶,那么最后剩2阶.....................
  7. android博学谷布局,Android项目实战系列—基于博学谷(四)我的模块(下)
  8. 将数字、字母、汉字分开的方法
  9. 洛谷 P1309 瑞士轮 归并
  10. Flask智能图书推荐系统