- 浏览: 31896 次
- 性别:
- 来自: 西安
-
最新评论
0-1背包问题:
给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包的总容量为c。
问:应该如何选择装入背包的物品,使得装入背包中物品的总价值最大?
在选择装入背包的物品时,对每种物品i只有两种选择,即装入背包或不装入背包,因此该问题称为0-1背包问题。
该问题的形式化描述是:
0-1背包问题具有最优子结构性质,可用动态规划法解决。
解法一:
动态规划
算法分析:
1)设m(i, j)是背包容量为j,可选物品为0,1,...,i时0-1背包问题的最优值。
2)递归式
m(i, j) = 0 j=0
m(i, j) = 0 i=0 && j < wi
m(i, j) = vi i=0 && j >= wi
m(i, j) = m(i-1, j) j < wi
m(i, j) = max{m(i-1, j), m(i-1, j-wi) + vi} j >= wi
实现:
/* description: * 0-1背包问题 动态规划求解 * 算法分析: * 1)设m(i, j)是背包容量为j,可选物品为0,1,...,i时0-1背包问题的最优值。 * 2)递归式 * m(i, j) = 0 j=0 * m(i, j) = 0 i=0 && j < wi * m(i, j) = vi i=0 && j >= wi * m(i, j) = m(i-1, j) j < wi * m(i, j) = max{m(i-1, j), m(i-1, j-wi) + vi} j >= wi * * auther:cm * date:2010/12/1 */ public class DynamicKnapsack { //背包容量 private int c; //物品重量数组 private int[] w; //物品价值数组 private int[] v; private int[][] m; //记录结果 private int[] x; //最大价值 private int maxV; public DynamicKnapsack(int[] w, int[] v, int c) { this.w = w; this.v = v; this.c = c; m = new int[w.length][c+1]; x = new int[w.length]; } public void knapsack() { init(); for (int i = 1; i < m.length; i++) { for (int j = 1; j < m[i].length; j++) { if (j < w[i]) { m[i][j] = m[i-1][j]; } else { m[i][j] = Math.max(m[i-1][j], m[i-1][j-w[i]] + v[i]); } } } maxV = m[m.length - 1][c]; //System.out.println(maxV + " "); } //初始化 private void init() { for (int i = 0; i < m.length; i++) { m[i][0] = 0; } for (int j = 0; j < m[0].length; j++) { if (w[0] <= j) { m[0][j] = v[0]; } else { m[0][j] = 0; } } } //得到最优解 public int[] getResult() { int tmp = c; int i; for (i = m.length - 1; i > 0; i--) { if (m[i][tmp] == m[i-1][tmp]) { x[i] = 0; } else { x[i] = 1; tmp = tmp - w[i]; } } x[i] = (m[0][c] != 0) ? 1 : 0; return x; } //打印数组m public void printM() { for (int i = 0; i < m.length; i++) { System.out.println(); for (int j = 0; j < m[i].length; j++) { System.out.print(m[i][j] + " "); } } } public static void main(String[] args) { int[] w = {3, 2, 5, 4, 1}; int[] v = {6, 5, 7, 3, 5}; //int[] w = {2, 2, 6, 5, 4}; //int[] v = {6, 3, 5, 4, 6}; int c = 10; DynamicKnapsack k = new DynamicKnapsack(w, v, c); k.knapsack(); int[] x = k.getResult(); for (int i = 0; i < x.length; i++) { System.out.print(x[i] + " "); } System.out.println(); k.printM(); } }
测试数据:
c = 10
w:3 2 5 4 1
v: 6 5 7 3 5
运行结果:
1 1 0 1 1
0 0 0 6 6 6 6 6 6 6 6
0 0 5 6 6 11 11 11 11 11 11
0 0 5 6 6 11 11 12 13 13 18
0 0 5 6 6 11 11 12 13 14 18
0 5 5 10 11 11 16 16 17 18 19
该算法缺点:
1)要求所给物品的重量是整数
2)当背包容量c很大时,算法时间复杂度很大
解法二:
回溯法
算法分析:
0-1背包问题是子集选取问题。一般情况下,0-1背包问题是NP难的。0-1背包问题的解空间可用子集树表示。在搜索解空间树时,只要其左儿子结点是一个可行结点,搜索就进入其左子树,当右子树有可能包含最优解时才进入右子树搜索,否则将右子树剪去。
设r是当前剩余物品价值总和,cv是当前价值,bestv是当前最优价值。当cv + r <= bestv时,可剪去右子树。
计算右子树中解的上界的更好方法是将剩余物品以其单位重量价值排序,然后依次装入物品,直至装不下时,在装入物品的一部分而装满背包。由此得到的价值是右子树中解的上界。为了便于计算上界,可先将物品以其单位重量价值由大到小排序,此后只要顺序的考察各物品即可。
算法实现如下:
BacktrackKnapsack.java
/* * 0-1背包问题 * 回溯法 * 0-1背包问题的解空间可用子集树表示。 * date: 2010/12/13 * auther:cm */ import java.util.Arrays; import java.util.Collections; public class BacktrackKnapsack { //背包容量 private double c; //物品数 private int count; //物品数组 private Goods[] goods; //当前重量 private double cw; //当前价值 private double cv; //当前最优价值 private double bestv; //用于构造最有解 private int[] x; //当前解 private int[] bestx; //当前最优解 public BacktrackKnapsack(double c, double[] w, double[] v) { this.c = c; this.count = w.length; x = new int[count]; bestx = new int[count]; goods = new Goods[count]; for (int i = 0; i < goods.length; i++) { goods[i] = new Goods(i, w[i], v[i]); } } //计算最优值 public double knapsack() { //按单位重量价值由大到小排序 Arrays.sort(goods, Collections.reverseOrder()); backtrack(0); return bestv; } //回溯算法 private void backtrack(int i) { //到达叶节点 if (i >= count) { System.out.println("到达叶节点.............."); if (cv > bestv) { System.out.println("更新数据.............."); bestv = cv; for (int j = 0; j < bestx.length; j++) { bestx[j] = x[j]; } } return; } //搜索子树 if ((cw + goods[i].getW()) <= c) { //进入左子树 x[goods[i].getId()] = 1; cw += goods[i].getW(); cv += goods[i].getV(); backtrack(i+1); cw -= goods[i].getW(); cv -= goods[i].getV(); } //搜索右子树,计算上界 裁剪不含最优解的子树 if (bound(i+1) > bestv) { backtrack(i+1); } } //计算上界,裁剪不含最优解的子树 private double bound(int i) { double cleft = c - cw; double bound = cv; //以单位重量价值递减顺序装入物品 while (i < count && goods[i].getW() <= cleft) { bound += goods[i].getV(); cleft -= goods[i].getW(); i++; } //装满背包 if (i < count) { bound += cleft * goods[i].getAverage(); } return bound; } //返回最优解 public int[] getResult() { return bestx; } //返回最优值 public double getBestv() { return bestv; } //测试 public static void main(String[] args) { double c = 10; //double[] w = {2,2,6,5,4}; //double[] v = {6,3,5,4,6}; double[] w = {2, 5, 3, 4, 1}; double[] v = {5, 7, 6, 3, 5}; BacktrackKnapsack k = new BacktrackKnapsack(c, w, v); double best = k.knapsack(); System.out.println(best); int[] result = k.getResult(); for (int i = 0; i < result.length; i++) { System.out.print(result[i] + " "); } } }
物品类
Goods.java
//物品类 public class Goods implements Comparable { //编号 private int id; //重量 private double w; //价值 private double v; //单位重量价值 private double average; public Goods(int id, double w, double v) { this.id = id; this.w = w; this.v = v; average = v / w; } public int compareTo(Object obj) { double aver = ((Goods)obj).getAverage(); if (average > aver) { return 1; } else if (average < aver) { return -1; } else { return 0; } } public void setId(int id) { this.id = id; } public int getId() { return id; } public void setW(double w) { this.w = w; } public double getW() { return w; } public void setV(double v) { this.v = v; } public double getV() { return v; } public double getAverage() { return average; } }
测试数据:
c = 10
double[] w = {2, 5, 3, 4, 1};
double[] v = {5, 7, 6, 3, 5};
运行结果:
到达叶节点..............
更新数据..............
19.0
1 0 1 1 1
发表评论
-
求组合数
2011-03-26 20:23 765求组合数 从n个数里面取m个数 (递归实现) /** ... -
背包问题
2010-12-14 18:27 955与0-1 背包问题类似,所 ... -
装载问题
2010-12-12 18:16 1476装载问题 有一批共n个集装箱要装上两艘载重量分别为c1和c2 ... -
最优装载问题
2010-12-12 11:13 1512问题描述: 有一批集装箱要装上一艘载重量为C的轮船,要求在装 ... -
喝汽水问题
2010-11-28 21:51 960问题描述: 1元钱一瓶汽水,喝完后两个空瓶换一瓶汽水,问:你 ... -
最大子段和(三)
2010-11-21 10:04 731最大子段和问题 解法三: 算法分析: 对于序列a,设j代 ... -
最大子段和(二)
2010-11-20 19:15 890最大子段和问题 解法二:分治法 算法分析: 最大子 ... -
最大子段和(一)
2010-11-20 10:31 937问题描述: 给定n个整数(可能为负数)组成的序列a1,a2, ... -
动态规划之最长单调递增子序列
2010-11-18 22:33 3556问题描述: 找出由n个数组成的序列的最长单调递增子序列 解 ... -
动态规划之最长公共子序列
2010-11-18 14:25 1393一个给定序列的子序列是在该序列中删去若干元素后得到的序列。若给 ... -
矩阵连乘问题
2010-11-16 21:39 1122/* description: * * 动态规划算法之 ... -
二分搜索法的简单实现
2010-10-20 09:11 678二分搜索算法是运用分治策略的典型例子。 基本思想:将n个元素 ... -
最小生成树之Prim算法
2010-10-16 22:45 1019Prim算法Java朴素版 //Prim算法求最小生成树,使 ...
相关推荐
0-1背包问题算法简洁易懂 0-1背包问题简介 0-1背包问题是算法设计中的一种经典问题。它是一个组合优化问题,属于NP-hard问题。问题的描述是:给定一组物品,每个物品都有一个价值和一个重量,要求在不超过背包容量...
利用回溯法解0-1背包问题讲解 利用回溯法解0-1背包问题是子集选取问题,0-1背包问题是NP难题,回溯法可以很好地解决背包问题,以期得到最优解。 回溯法概念: * 回溯法是一个既带有系统性又带有跳跃性的搜索算法...
0-1背包问题是一种经典的组合优化问题,在计算机科学和运筹学中有着广泛的应用。它描述的是这样的场景:你有一个背包,其容量有限,而有一系列物品,每个物品都有自己的重量和价值。你的目标是在不超过背包容量的...
0-1背包问题是一个经典的优化问题,主要涉及动态规划算法的运用。在这个实验报告中,学生使用Java语言解决了一个0-1背包问题的实例。以下是关于这个问题和解决方案的详细解释。 一、问题描述: 0-1背包问题的核心是...
0-1背包问题是一种经典的组合优化问题,在计算机科学和运筹学中有着广泛的应用。它源于实际生活中的装箱问题,比如如何在限定的重量限制下,选择物品以获得最大价值。在这里,我们主要探讨0-1背包问题的定义、算法...
### 动态规划法与回溯法解决0-1背包问题 #### 一、实验目的与背景 0-1背包问题是一种经典的组合优化问题,在实际应用中有着广泛的用途,例如资源分配、投资组合等问题都可以抽象成背包问题的形式。本实验旨在通过...
动态规划是一种有效的算法设计策略,尤其适用于解决具有重叠子问题和最优子结构的问题,而0-1背包问题就是这种策略的一个典型应用。0-1背包问题是一个经典的组合优化问题,它源自实际生活中的资源分配问题,比如在...
### 0-1背包问题分支界限法求解——C语言实现 #### 一、背景介绍 在计算机科学中,背包问题是一类优化问题的经典案例,它涉及到如何在满足一定约束条件下(例如背包的最大承重),从一系列物品中选择最优组合以达到...
0-1背包问题是一种经典的组合优化问题,在计算机科学和运筹学中有着广泛的应用。它模拟了实际生活中的资源分配问题,例如在有限的背包容量下,如何选择物品以最大化价值。在这个问题中,每个物品都有一个重量和一个...
0-1背包问题是一种经典的组合优化问题,在计算机科学和运筹学中有着广泛的应用。它描述的是这样的场景:有一组物品,每个物品有自己的价值和重量,且每件物品只能选择0次或1次(不能部分选取)。目标是在不超过背包...
动态规划求解0-1背包问题的改进算法完整解释 在计算机算法设计与分析中,动态规划法是解决背包问题的常用方法之一。所谓背包问题,是指在有限的背包容量下,如何选择物品来达到最大价值的问题。在本文中,我们将对...
利用动态规划方法求解经典0-1背包问题,仅供参考,欢迎指正
0-1背包问题是一种经典的组合优化问题,在计算机科学和运筹学中有着广泛的应用。它模拟了在资源有限的情况下如何选择物品以最大化总体价值。在这个问题中,我们有一组物品,每件物品都有一个重量和一个价值,目标是...
0-1背包问题是一种经典的组合优化问题,在许多实际场景中都有应用,比如资源分配、生产计划、投资组合优化等。该问题的基本设定是:有一组物品,每件物品都有一个重量和一个价值,且每件物品只能选择放入背包或者不...
0-1背包问题是一个经典的组合优化问题,常用于资源有限情况下的最优决策。在这个问题中,我们有n种物品,每种物品i有一个重量wi和一个价值vi,还有一个背包,它的容量是C。目标是选择一些物品放入背包,使得放入背包...