• 再谈0-1背包问题


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

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

  • 相关阅读:
    nginx 安装配置
    mysql分表
    冲刺day7
    冲刺day6
    冲刺day5
    冲刺day4
    冲刺day3
    冲刺day2
    冲刺day1
    撰写《需求规格说明书》的工作流程、组员分工和组员工作量比例
  • 原文地址:https://www.cnblogs.com/wxisme/p/4922215.html
Copyright © 2020-2023  润新知