1 问题描述

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

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

2 解决方案

2.1 蛮力法


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

package com.liuzhen.chapterThree;

public class Knapsack {

    public int maxSumValue = 0;        //定义满足背包问题子集的最大承重所得的总价值,初始化为0
/*
* 数组A的行数为2^n,代表n个物品共有2^n个子集,列数为n。即每一行的排列为一个背包实例
* 数组weight存放每个物品的具体重量
* 数组value存放每个物品的具体价值
* n代表共有n个物品
* maxWeight表示背包最大承重量
*/
public void bruteForce(int[][] A,int[] weight,int[] value,int n,int maxWeight){ for(int i = 0;i < Math.pow(2, n);i++){ //总共有2^n个子集,需要进行2^n次循环,及数组A有2^n行
int temp1 = i;
for(int j = 0;j < n;j++){ //数组A有n列,每一列代表一个物品
int temp2 = temp1%2;
A[i][j] = temp2;
temp1 = temp1/2;
}
} printArray(A,weight,value,maxWeight); } //输出穷举方案的背包实例的选择物品(0代表不包含该物品,1表示包含该物品)的总重量及总价值,并输出最优实例的总价值
public void printArray(int[][] A,int[] weight,int[] value,int maxWeight){
int len1 = A.length; //二维数组的行数
int len2 = A[0].length; //二维数组的列数
for(int i = 0;i < len1;i++){
int tempWeight = 0; //暂时计算当前选中背包实例物品的总重量,初始化为0
int tempSumValue = 0; //暂时计算当前选中背包实例物品的总价值,初始化为0
for(int j = 0;j < len2;j++){
System.out.print(A[i][j]+" ");
// if(A[i][j] != 0)
// System.out.print(" 物品"+j);
tempWeight += A[i][j]*weight[j];
tempSumValue += A[i][j]*value[j];
}
System.out.print("\t"+"总重量为:"+tempWeight);
if(tempWeight <= maxWeight)
System.out.print("\t"+"总价值为:"+tempSumValue);
else
System.out.print("\t"+"不可行(超出背包最大承重)");
if(tempWeight <= maxWeight && tempSumValue > maxSumValue)
maxSumValue = tempSumValue;
System.out.println();
}
System.out.println("穷举查找得知,最优解的总价值为:"+maxSumValue);
} public static void main(String[] args){
Knapsack test = new Knapsack();
int[][] A = new int[16][4];
int[] weight = {7,3,4,5};
int[] value = {42,12,40,25};
test.bruteForce(A,weight,value,4,10); //背包的承重最大为10
} }

运行结果:

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

2.2 减治法

2.2.1 递归求解


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

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

package com.liuzhen.chapter4;

import java.util.LinkedList;
import java.util.List; public class GrayCode {
//递归求取n个不同物品的所有子集
public String[] getGrayCode2(int n){
int len = (int) Math.pow(2, n);
String[] result = new String[len];
if(n == 1){
result[0] = "0";
result[1] = "1";
return result;
}
String[] temp = getGrayCode2(n-1); //递归求取n-1个不同物品的所有子集
for(int i = 0;i < temp.length;i++){ //根据格雷码去掉最高位,前一半和后一半二进制数完全一样的对称性
result[i] = "0" + temp[i]; //前一半格雷码,最高位为0
result[result.length-1-i] = "1" + temp[i]; //后一半格雷码,最高位为1
}
return result;
} public static void main(String[] args){
GrayCode test = new GrayCode();
String[] temp2 = test.getGrayCode2(3);
System.out.println("使用递归求解n个物品所有子集结果如下:");
for(int i = 0;i < temp2.length;i++)
System.out.println(temp2[i]);
}
}

运行结果:

使用递归求解n个物品所有子集结果如下:
001
010
111
100

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

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

package com.liuzhen.chapter4;

import java.util.LinkedList;
import java.util.List; public class GrayCode {
//运用异或运算得到n个不同物品的所有子集
public List<Integer> getGaryCode1(int n){
List<Integer> result = new LinkedList<>();
if(n >= 0){
result.add(0);
int top = 1;
for(int i = 0;i < n;i++){
System.out.print("result.size() = "+result.size()+" ");
for(int j = result.size()-1;j >= 0;j--){
System.out.print("result.get("+j+")^top = "+result.get(j)+"^"+top+" = "+(result.get(j)^top)+" ");
result.add(result.get(j)^top); //符号‘^’是异或运算(使用具体数字的二进制进行运算),即1^0=1,0^1=1,0^0=0,1^1=0
}
System.out.println();
top <<= 1; //top二进制左移1位,相当于top=top*2
System.out.println("top = "+top);
}
}
return result;
}
//把十进制数转换成长度为n的二进制数
public StringBuffer[] getBinary(List<Integer> A,int n){
StringBuffer[] result = new StringBuffer[A.size()];
for(int i = 0;i < A.size();i++){
int temp1 = A.get(i);
int judge = n;
char[] temp2 = new char[n]; //用于存放temp1的n位二进制数
while(judge > 0){
int temp3 = temp1%2;
temp2[judge-1] = (char) (temp3+48); //对照char的unicode编码,把int型数字转换为char型
temp1 = temp1/2;
judge--;
}
result[i] = new StringBuffer(String.valueOf(temp2));
}
return result;
} public static void main(String[] args){
GrayCode test = new GrayCode();
List<Integer> temp = test.getGaryCode1(3);
System.out.println(temp);
StringBuffer[] temp1 = test.getBinary(temp, 3);
for(int i = 0;i < temp1.length;i++)
System.out.println(temp1[i]);
}
}

运行结果:

result.size() = 1  result.get(0)^top = 0^1 = 1
top = 2
result.size() = 2 result.get(1)^top = 1^2 = 3 result.get(0)^top = 0^2 = 2
top = 4
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
top = 8
[0, 1, 3, 2, 6, 7, 5, 4]
001
010
111
100

2.3 动态规划法

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

package com.liuzhen.chapter8;

public class MFKnapsack {
/*
* 参数weight:物品1到物品n的重量,其中weight[0] = 0
* 参数value:物品1到物品n的价值,其中value[0] = 0
* 函功能:返回背包重量从0到所有物品重量之和区间的每一个重量所能达到的最大价值
*/
public int[][] getMaxValue(int[] weight, int[] value) {
int lenRow = weight.length;
int lenColumn = 0;
for(int i = 0;i < weight.length;i++)
lenColumn += weight[i];
int[][] F = new int[lenRow][lenColumn+1]; //列值长度加1,是因为最后一列要保证重量值为lenColumn
for(int i = 1;i < weight.length;i++) {
for(int j = 1;j <= lenColumn;j++) {
if(j < weight[i])
F[i][j] = F[i-1][j];
else {
if(F[i-1][j] > F[i-1][j-weight[i]] + value[i])
F[i][j] = F[i-1][j];
else
F[i][j] = F[i-1][j-weight[i]] + value[i];
}
}
}
return F;
} public static void main(String[] args) {
MFKnapsack test = new MFKnapsack();
int[] weight = {0,2,1,3,2};
int[] value = {0,12,10,20,15};
int[][] F = test.getMaxValue(weight, value);
System.out.println("背包承重从0到所有物品重量之和为8的承重能够达到的最大价值分别为:");
for(int i = 0;i < F.length;i++) {
for(int j = 0;j < F[0].length;j++)
System.out.print(F[i][j]+"\t");
System.out.println();
}
}
}

运行结果:

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

Java实现背包问题的更多相关文章

  1. Java数据结构和算法 - 递归

    三角数字 Q: 什么是三角数字? A: 据说一群在毕达哥拉斯领导下工作的古希腊的数学家,发现了在数学序列1,3,6,10,15,21,……中有一种奇特的联系.这个数列中的第N项是由第N-1项加N得到的 ...

  2. Spark案例分析

    一.需求:计算网页访问量前三名 import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} /* ...

  3. 蓝桥杯 0/1背包问题 (java)

      今天第一次接触了0/1背包问题,总结一下,方便以后修改.不对的地方还请大家不啬赐教! 上一个蓝桥杯的例题: 数据规模和约定 代码: import java.util.Scanner; public ...

  4. Java实现动态规划法求解0/1背包问题

    摘要: 使用动态规划法求解0/1背包问题. 难度: 初级 0/1背包问题的动态规划法求解,前人之述备矣,这里所做的工作,不过是自己根据理解实现了一遍,主要目的还是锻炼思维和编程能力,同时,也是为了增进 ...

  5. 算法笔记_019:背包问题(Java)

    目录 1 问题描述 2 解决方案 2.1 蛮力法 2.2 减治法 2.2.1 递归求解 2.2.2 非递归求解(运用异或运算) 2.3 动态规划法 1 问题描述 给定n个重量为w1,w2,w3,... ...

  6. java实现最通俗易懂的01背包问题

    这几天一直在想背包问题,昨天还有个学长专门讲了,但是还是不是很理解,今天我终于想通了背包问题,其实只要理解了这个思路,不管用什么语言,肯定是能编出来的.下面我就来介绍一下背包问题. 1.题目描述: 有 ...

  7. 0-1背包问题蛮力法求解(java版本)

    sloves: package BackPack; public class Solves {  public int[] DecimaltoBinary(int n,int m)  {   int ...

  8. 01背包问题(Java实现)

    关于背包问题,百度文库上有崔添翼大神的<背包九讲>,不明的请移步查看.这里仅介绍最基本的01背包问题的实现. public class Knapsack { private final i ...

  9. [算法]用java实现0-1背包和部分背包问题

    问题描述: 0-1背包问题,部分背包问题(课本P229)实验要求: (1)实现0-1背包的动态规划算法求解 (2)实现部分背包的贪心算法求解 0-1背包问题代码: public static void ...

随机推荐

  1. [hdu5312]数的拆分,数学推导

    题意:给定一个序列,a[n]=3n(n-1)+1,n>=1,求给定的m(m<=1e9)最少可以用几个a里面的数表示(可以重复) 思路:对答案分类 (1)假定答案为1,则m必定是a中的某一个 ...

  2. python 基础应用2

    1.格式化输出% name = input('请输入姓名:') age = input('请输入年龄:') job = input('请输入工作:') mas = '''---------- info ...

  3. javaweb学习之路(3)Cookie

    1.Cookies的原理 1)首先浏览器向服务器发出请求. 2)服务器就会根据需要生成一个Cookie对象,并且把数据保存在该对象内. 3)然后把该Cookie对象放在响应头,一并发送回浏览器. 4) ...

  4. ql的python学习之路-day3

    字典操作 特性: 1.无序的 2.key是唯一的 , ,,], ,,], ,,], }, ,,], 'bbb' : ['a', 'b', 'c'], }}

  5. sqlservere小计合计总计

    SELECT CASE WHEN GROUPING(F1) = 1 THEN '总计' WHEN GROUPING(F1) = 0 AND GROUPING(F2) = 1 THEN F1+'合计' ...

  6. Zookeeper 如何保证分布式系统数据一致性

    写在前面 分布式架构出现后,越来越多的分布式系统会面临数据一致性的问题.目前,ZooKeeper 是在解决分布式数据一致性上最成熟稳定且被大规模应用的工业级解决方案. ZooKeeper 保证 分布式 ...

  7. css实现双色饼图

    from:wx--前端早读课 首先回想用css画三角形的方法: <div class="triangle"></div> .triangle { displ ...

  8. Flutter仿掘金点赞效果

    老孟导读:今天分享一下如何实现掘金点赞效果,这不仅仅是一篇技术文章,还是一篇解决问题思路的文章,遇到一个需求时,如何拆分需求,然后一步一步实现,这个过程比单纯的技术(此文)更有含金量. 先来看一下掘金 ...

  9. Django模板之模板变量

    深度查询句点符(.)在模板语言中有特殊的含义. 当模版系统遇到点("."),它将以这样的顺序查询: 字典查询(Dictionary lookup) 属性或方法查询(Attribut ...

  10. Spring JDBC 框架 简介

    在使用普通的 JDBC 数据库时,就会很麻烦的写不必要的代码来处理异常,打开和关闭数据库连接等. 但 Spring JDBC 框架负责所有的低层细节,从开始打开连接,准备和执行 SQL 语句,处理异常 ...