上次解0-1背包问题用的是动态规划法:http://www.cnblogs.com/wxisme/p/4898057.html
这次用回溯法来解。
01背包问题:给定n种物品和一背包。物品i的重量是wi其价值为vi,背包的容量为c。问如何选择装入背包的物品,使得装入背包中物品的总价值最大?
在选择装入背包的物品时,对每种物品i只有两种选择,即装入或者不装入。不能将物品i装入背包多次也不能只装部分的物品i。因此成为01背包问题。
思路:用回溯法递归的搜索问题的解空间树,选择最优解。
以n = 4, c = 7, v = {9, 10, 7, 4} w = {3, 5, 2, 1};为例。
解空间树为:
深度优先递归遍历解空间树,用x[i]来记录第i层节点是否被选择。用best[i]来记录当前最优路径是否包含i节点。
递归到每个叶子节点时判断是否超出背包容量并记录最优值。当递归遍历完毕,best[i]为1节点的集合就是最优解。
实现代码如下:
package BackTrack_01Package; /** *@Description:TODO<p>回溯法解0-1背包问题 </p><br/> *@author 王旭 *@time 2015-10-29 下午10:31:45 */ public class BackTrack_01Package { public int n; //物品的个数 public int c; //背包的容量 public int maxValue; //最大价值,当前最优。 public int[] v; //物品的价值 public int[] w; //物品的重量 public int[] x; //物品是否被选 public int[] best; //最优路径 public BackTrack_01Package(int n, int c, int[] v, int[] w) { super(); this.n = n; this.c = c; this.v = v; this.w = w; this.x = new int[n]; this.best = new int[n]; } /** * * @param i * @param cv * @param cw */ public void backtrack(int i, int cv, int cw) { //回溯到解空间树的叶子节点 if(i >= n) { if(cv > maxValue) { maxValue = cv; for(int k=0; k<n; k++) { best[k] = x[k]; } } return; } for(int j=0;j<=1;j++) { x[i]=j; if(cw + x[i] * w[i] <= c) { cw += w[i] * x[i]; cv += v[i] * x[i]; backtrack(i+1, cv, cw); cw -= w[i] * x[i]; cv -= v[i] * x[i]; } } } public void printSolution() { System.out.println("选中的物品编号:"); for(int i : best) { System.out.print(i + " "); } System.out.println(); System.out.println("最大价值:" + maxValue); } }
测试代码:
public static void main(String[] args) { int n = 4, c = 7; int[] v = new int[]{9, 10, 7, 4}; int[] w = new int[]{3, 5, 2, 1}; BackTrack_01Package b = new BackTrack_01Package(n ,c, v, w); b.backtrack(0, 0, 0); b.printSolution(); }
运行结果:
选中的物品编号: 1 0 1 1 最大价值:20
代码还可以再优化,其实在一般情况下,第一次深度搜索就是所有节点的状态都是0,就是都不选很明显这不可能是最优解。所以类似的地方还可以用剪枝来优化没有必要的计算。
等有时间把剪枝的代码补上。。。