首页 > 技术文章 > 再谈0-1背包问题

wxisme 2015-10-30 20:23 原文

上次解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,就是都不选很明显这不可能是最优解。所以类似的地方还可以用剪枝来优化没有必要的计算。

等有时间把剪枝的代码补上。。。

 

推荐阅读