目录

1 问题描述

2 解决方案

2.1 蛮力法

2.2 减治法

2.2.1 递归求解

2.2.2 非递归求解(运用异或运算)

2.3 动态规划法


1 问题描述

给定n个重量为w1,w2,w3,...,wn,价值为v1,v2,...,vn的物品和一个承重为W的背包,求这些物品中最有价值的子集(PS:每一个物品要么选一次,要么不选),并且要能够装到背包。

附形象描述:就像一个小偷打算把最有价值的赃物装入他的背包一样,但如果大家不喜欢扮演小偷的角色,也可以想象为一架运输机打算把最有价值的物品运输到外地,同时这些物品的重量不能超出它的运输能力。


2 解决方案

2.1 蛮力法

使用蛮力法解决包含n个物品的背包问题,首先得求出这n个物品的所有子集,对于每一个物品存在两种情况:选中(在程序中用1表示),未选中(在程序中用0表示)。该n个物品的所有子集数数量为2^n。下面请看一个简单示例:

此处,使用一个二维数组存放所有子集,数组的每一行代表一个子集,每一列代表一个具体物品。

  1. package com.liuzhen.chapterThree;
  2.  
  3. public class Knapsack {
  4.  
  5. public int maxSumValue = 0; //定义满足背包问题子集的最大承重所得的总价值,初始化为0
  6. /*
  7. * 数组A的行数为2^n,代表n个物品共有2^n个子集,列数为n。即每一行的排列为一个背包实例
  8. * 数组weight存放每个物品的具体重量
  9. * 数组value存放每个物品的具体价值
  10. * n代表共有n个物品
  11. * maxWeight表示背包最大承重量
  12. */
  13. public void bruteForce(int[][] A,int[] weight,int[] value,int n,int maxWeight){
  14.  
  15. for(int i = 0;i < Math.pow(2, n);i++){ //总共有2^n个子集,需要进行2^n次循环,及数组A有2^n行
  16. int temp1 = i;
  17. for(int j = 0;j < n;j++){ //数组A有n列,每一列代表一个物品
  18. int temp2 = temp1%2;
  19. A[i][j] = temp2;
  20. temp1 = temp1/2;
  21. }
  22. }
  23.  
  24. printArray(A,weight,value,maxWeight);
  25.  
  26. }
  27.  
  28. //输出穷举方案的背包实例的选择物品(0代表不包含该物品,1表示包含该物品)的总重量及总价值,并输出最优实例的总价值
  29. public void printArray(int[][] A,int[] weight,int[] value,int maxWeight){
  30. int len1 = A.length; //二维数组的行数
  31. int len2 = A[0].length; //二维数组的列数
  32. for(int i = 0;i < len1;i++){
  33. int tempWeight = 0; //暂时计算当前选中背包实例物品的总重量,初始化为0
  34. int tempSumValue = 0; //暂时计算当前选中背包实例物品的总价值,初始化为0
  35. for(int j = 0;j < len2;j++){
  36. System.out.print(A[i][j]+" ");
  37. // if(A[i][j] != 0)
  38. // System.out.print(" 物品"+j);
  39. tempWeight += A[i][j]*weight[j];
  40. tempSumValue += A[i][j]*value[j];
  41. }
  42. System.out.print("\t"+"总重量为:"+tempWeight);
  43. if(tempWeight <= maxWeight)
  44. System.out.print("\t"+"总价值为:"+tempSumValue);
  45. else
  46. System.out.print("\t"+"不可行(超出背包最大承重)");
  47. if(tempWeight <= maxWeight && tempSumValue > maxSumValue)
  48. maxSumValue = tempSumValue;
  49. System.out.println();
  50. }
  51. System.out.println("穷举查找得知,最优解的总价值为:"+maxSumValue);
  52. }
  53.  
  54. public static void main(String[] args){
  55. Knapsack test = new Knapsack();
  56. int[][] A = new int[16][4];
  57. int[] weight = {7,3,4,5};
  58. int[] value = {42,12,40,25};
  59. test.bruteForce(A,weight,value,4,10); //背包的承重最大为10
  60. }
  61.  
  62. }

运行结果:

  1. 0 0 0 0 总重量为:0 总价值为:0
  2. 1 0 0 0 总重量为:7 总价值为:42
  3. 0 1 0 0 总重量为:3 总价值为:12
  4. 1 1 0 0 总重量为:10 总价值为:54
  5. 0 0 1 0 总重量为:4 总价值为:40
  6. 1 0 1 0 总重量为:11 不可行(超出背包最大承重)
  7. 0 1 1 0 总重量为:7 总价值为:52
  8. 1 1 1 0 总重量为:14 不可行(超出背包最大承重)
  9. 0 0 0 1 总重量为:5 总价值为:25
  10. 1 0 0 1 总重量为:12 不可行(超出背包最大承重)
  11. 0 1 0 1 总重量为:8 总价值为:37
  12. 1 1 0 1 总重量为:15 不可行(超出背包最大承重)
  13. 0 0 1 1 总重量为:9 总价值为:65
  14. 1 0 1 1 总重量为:16 不可行(超出背包最大承重)
  15. 0 1 1 1 总重量为:12 不可行(超出背包最大承重)
  16. 1 1 1 1 总重量为:19 不可行(超出背包最大承重)
  17. 穷举查找得知,最优解的总价值为:65

2.2 减治法

2.2.1 递归求解

背包问题的实质是求取n个不同物品的所有子集,在此基础上寻找重量合适,总价值最大的子集。此处只给出如何求出n个不同物品的所有子集实现,至于如何寻找符合背包问题的子集,感兴趣的同学可以自己动手实现以下哟~

此处是运用减治法思想,根据二进制反射格雷码的算法思想,来实现此问题。具体解释,请看下面一段出自《算法设计与分析基础》第三版上讲解:

具体代码如下:

  1. package com.liuzhen.chapter4;
  2.  
  3. import java.util.LinkedList;
  4. import java.util.List;
  5.  
  6. public class GrayCode {
  7. //递归求取n个不同物品的所有子集
  8. public String[] getGrayCode2(int n){
  9. int len = (int) Math.pow(2, n);
  10. String[] result = new String[len];
  11. if(n == 1){
  12. result[0] = "0";
  13. result[1] = "1";
  14. return result;
  15. }
  16. String[] temp = getGrayCode2(n-1); //递归求取n-1个不同物品的所有子集
  17. for(int i = 0;i < temp.length;i++){ //根据格雷码去掉最高位,前一半和后一半二进制数完全一样的对称性
  18. result[i] = "0" + temp[i]; //前一半格雷码,最高位为0
  19. result[result.length-1-i] = "1" + temp[i]; //后一半格雷码,最高位为1
  20. }
  21. return result;
  22. }
  23.  
  24. public static void main(String[] args){
  25. GrayCode test = new GrayCode();
  26. String[] temp2 = test.getGrayCode2(3);
  27. System.out.println("使用递归求解n个物品所有子集结果如下:");
  28. for(int i = 0;i < temp2.length;i++)
  29. System.out.println(temp2[i]);
  30. }
  31. }

运行结果:

  1. 使用递归求解n个物品所有子集结果如下:
  2. 000
  3. 001
  4. 011
  5. 010
  6. 110
  7. 111
  8. 101
  9. 100

2.2.2 非递归求解(运用异或运算)

此处也使用求取格雷码的思想,完成求取n个物品的所有子集,不过此处是使用非递归来实现,运用异或运算,其构造非常巧妙,个人感觉要理解这种编码方式和思想得多多运用,直至熟能生巧。

具体代码如下:

  1. package com.liuzhen.chapter4;
  2.  
  3. import java.util.LinkedList;
  4. import java.util.List;
  5.  
  6. public class GrayCode {
  7. //运用异或运算得到n个不同物品的所有子集
  8. public List<Integer> getGaryCode1(int n){
  9. List<Integer> result = new LinkedList<>();
  10. if(n >= 0){
  11. result.add(0);
  12. int top = 1;
  13. for(int i = 0;i < n;i++){
  14. System.out.print("result.size() = "+result.size()+" ");
  15. for(int j = result.size()-1;j >= 0;j--){
  16. System.out.print("result.get("+j+")^top = "+result.get(j)+"^"+top+" = "+(result.get(j)^top)+" ");
  17. result.add(result.get(j)^top); //符号‘^’是异或运算(使用具体数字的二进制进行运算),即1^0=1,0^1=1,0^0=0,1^1=0
  18. }
  19. System.out.println();
  20. top <<= 1; //top二进制左移1位,相当于top=top*2
  21. System.out.println("top = "+top);
  22. }
  23. }
  24. return result;
  25. }
  26. //把十进制数转换成长度为n的二进制数
  27. public StringBuffer[] getBinary(List<Integer> A,int n){
  28. StringBuffer[] result = new StringBuffer[A.size()];
  29. for(int i = 0;i < A.size();i++){
  30. int temp1 = A.get(i);
  31. int judge = n;
  32. char[] temp2 = new char[n]; //用于存放temp1的n位二进制数
  33. while(judge > 0){
  34. int temp3 = temp1%2;
  35. temp2[judge-1] = (char) (temp3+48); //对照char的unicode编码,把int型数字转换为char型
  36. temp1 = temp1/2;
  37. judge--;
  38. }
  39. result[i] = new StringBuffer(String.valueOf(temp2));
  40. }
  41. return result;
  42. }
  43.  
  44. public static void main(String[] args){
  45. GrayCode test = new GrayCode();
  46. List<Integer> temp = test.getGaryCode1(3);
  47. System.out.println(temp);
  48. StringBuffer[] temp1 = test.getBinary(temp, 3);
  49. for(int i = 0;i < temp1.length;i++)
  50. System.out.println(temp1[i]);
  51. }
  52. }

运行结果:

  1. result.size() = 1 result.get(0)^top = 0^1 = 1
  2. top = 2
  3. result.size() = 2 result.get(1)^top = 1^2 = 3 result.get(0)^top = 0^2 = 2
  4. top = 4
  5. result.size() = 4 result.get(3)^top = 2^4 = 6 result.get(2)^top = 3^4 = 7 result.get(1)^top = 1^4 = 5 result.get(0)^top = 0^4 = 4
  6. top = 8
  7. [0, 1, 3, 2, 6, 7, 5, 4]
  8. 000
  9. 001
  10. 011
  11. 010
  12. 110
  13. 111
  14. 101
  15. 100

2.3 动态规划法

此处编码思想主要参考自《算法设计与分析基础》第三版的一段讲解,具体如下:

具体代码如下:

  1. package com.liuzhen.chapter8;
  2.  
  3. public class MFKnapsack {
  4. /*
  5. * 参数weight:物品1到物品n的重量,其中weight[0] = 0
  6. * 参数value:物品1到物品n的价值,其中value[0] = 0
  7. * 函功能:返回背包重量从0到所有物品重量之和区间的每一个重量所能达到的最大价值
  8. */
  9. public int[][] getMaxValue(int[] weight, int[] value) {
  10. int lenRow = weight.length;
  11. int lenColumn = 0;
  12. for(int i = 0;i < weight.length;i++)
  13. lenColumn += weight[i];
  14. int[][] F = new int[lenRow][lenColumn+1]; //列值长度加1,是因为最后一列要保证重量值为lenColumn
  15. for(int i = 1;i < weight.length;i++) {
  16. for(int j = 1;j <= lenColumn;j++) {
  17. if(j < weight[i])
  18. F[i][j] = F[i-1][j];
  19. else {
  20. if(F[i-1][j] > F[i-1][j-weight[i]] + value[i])
  21. F[i][j] = F[i-1][j];
  22. else
  23. F[i][j] = F[i-1][j-weight[i]] + value[i];
  24. }
  25. }
  26. }
  27. return F;
  28. }
  29.  
  30. public static void main(String[] args) {
  31. MFKnapsack test = new MFKnapsack();
  32. int[] weight = {0,2,1,3,2};
  33. int[] value = {0,12,10,20,15};
  34. int[][] F = test.getMaxValue(weight, value);
  35. System.out.println("背包承重从0到所有物品重量之和为8的承重能够达到的最大价值分别为:");
  36. for(int i = 0;i < F.length;i++) {
  37. for(int j = 0;j < F[0].length;j++)
  38. System.out.print(F[i][j]+"\t");
  39. System.out.println();
  40. }
  41. }
  42. }

运行结果:

  1. 背包承重从0到所有物品重量之和为8的承重能够达到的最大价值分别为:
  2. 0 0 0 0 0 0 0 0 0
  3. 0 0 12 12 12 12 12 12 12
  4. 0 10 12 22 22 22 22 22 22
  5. 0 10 12 22 30 32 42 42 42
  6. 0 10 15 25 30 37 45 47 57

参考资料:

1. java实现格雷码生成

2.背包问题九讲

3.《算法设计与分析基础》第3版   Anany Levitin 著   潘彦 译

算法笔记_019:背包问题(Java)的更多相关文章

  1. 算法笔记_041:寻找和为定值的多个数(Java)

    目录 1 问题描述 2 解决方案 1 问题描述 输入两个整数n和sum,要求从数列1,2,3,...,n中随意取出几个数,使得它们的和等于sum,请将其中所有可能的组合列出来. 2 解决方案 上述问题 ...

  2. 学习Java 以及对几大基本排序算法(对算法笔记书的研究)的一些学习总结(Java对算法的实现持续更新中)

    Java排序一,冒泡排序! 刚刚开始学习Java,但是比较有兴趣研究算法.最近看了一本算法笔记,刚开始只是打算随便看看,但是发现这本书非常不错,尤其是对排序算法,以及哈希函数的一些解释,让我非常的感兴 ...

  3. 算法笔记_067:蓝桥杯练习 算法训练 安慰奶牛(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 Farmer John变得非常懒,他不想再继续维护供奶牛之间供通行的道路.道路被用来连接N个牧场,牧场被连续地编号为1到N.每一个牧场都是 ...

  4. 算法笔记(c++)--完全背包问题

    算法笔记(c++)--完全背包和多重背包问题 完全背包 完全背包不同于01背包-完全背包里面的东西数量无限 假设现在有5种物品重量为5,4,3,2,1  价值为1,2,3,4,5  背包容量为10 # ...

  5. 算法笔记(c++)--01背包问题

    算法笔记(c++)--经典01背包问题 算法解释起来太抽象了.也不是很好理解,最好的办法就是一步步写出来. 背包问题的核心在于m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+ ...

  6. 算法笔记_023:拓扑排序(Java)

    目录 1 问题描述 2 解决方案 2.1 基于减治法实现 2.2 基于深度优先查找实现 1 问题描述 给定一个有向图,求取此图的拓扑排序序列. 那么,何为拓扑排序? 定义:将有向图中的顶点以线性方式进 ...

  7. 算法笔记_018:旅行商问题(Java)

    目录 1 问题描述 2 解决方案 2.1 蛮力法 2.2 减治法 2.2.1 Johson-Trotter算法 2.2.2 基于字典序的算法   1 问题描述 何为旅行商问题?按照非专业的说法,这个问 ...

  8. 算法笔记_083:蓝桥杯练习 合并石子(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 在一条直线上有n堆石子,每堆有一定的数量,每次可以将两堆相邻的石子合并,合并后放在两堆的中间位置,合并的费用为两堆石子的总数.求把所有石子 ...

  9. 算法笔记_075:蓝桥杯练习 最短路(Java)

    目录 1 问题描述 2 解决方案 2.1 floyd算法解决 2.2 spfa算法解决   1 问题描述 问题描述 给定一个n个顶点,m条边的有向图(其中某些边权可能为负,但保证没有负环).请你计算从 ...

随机推荐

  1. CF1009G Allowed Letters

    link 题意: 给你一个长为n的串,字符集'a'~'f'.你可以重排这个串,满足指定m个位置上只能放特定的字符,m个位置以及字符集会给出.求字典序最小的串? $n,m\leq 10^5.$ 题解: ...

  2. [CF115E]Linear Kingdom Races

    [CF115E]Linear Kingdom Races 题目大意: 有\(n(n\le10^5)\)个物品,编号为\(1\sim n\).选取第\(i\)个物品需要\(c_i\)的代价.另外有\(m ...

  3. 斐波那契数列(Java实现)

    描述 一个斐波那契序列,F(0) = 0, F(1) = 1, F(n) = F(n-1) + F(n-2) (n>=2),根据n的值,计算斐波那契数F(n),其中0≤n≤1000. 输入 输入 ...

  4. MySql - GROUP BY 和 HAVING关键字

    本文主要针对GROUP BY 和 HAVING两个关键字进行分析 使用测试表如下: 测试前,只知道GROUP BY 这个关键字的作用是根据一个字段进行分组,去重.HAVING对分组设置条件. 具体分组 ...

  5. [转]ViewPager 详解(三)---PagerTabStrip与PagerTitleStrip添加标题栏的异同

      目录(?)[-] 一PagerTitleStrip Class Overview XML布局文件 重写适配器的getPageTitle函数 变量 初始化 重写CharSequence getPag ...

  6. [转]Intel haxm安装失败问题解决

    在安装Intel haxm为安卓模拟器加速时,会遇到提示VT-X未开启问题,问题提示如下图 工具/原料 Intel haxm 安卓模拟器 方法/步骤 1 确认你的处理器是否是Intel的,如果是AMD ...

  7. Codeforces Round #194 (Div. 1) B. Chips 水题

    B. Chips Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/333/problem/B D ...

  8. Webpack使用指南

    Webpack 是当下最热门的前端资源模块化管理和打包工具. 什么是webpack Webpack 是当下最热门的前端资源模块化管理和打包工具.它可以将许多松散的模块按照依赖和规则打包成符合生产环境部 ...

  9. iOS开源项目大全

    UI界面类项目: Panoramagl —— 720全景展示 Panorama viewer library for iPhone, iPad and iPod touch MBProgressHUD ...

  10. Buck converter uses low-side PWM IC

    The most common switching-power topology is a buck converter, which efficiently transforms high volt ...