0-1背包问题分支定限法Java实现

本文由此博客更改而来,对此博客的解法进行了部分修改,代码结构尚有很多不足之处,敬请原谅~

具体实现请看代码及其注释:

定义的一个节点对象,封装v与w等信息,将被作为二叉树节点使用

class ItemNode implements Comparable<ItemNode>{  
    int weight;//该节点目前背包中的重量 
    double value;//该节点目前背包中的总价值 
    double upprofit;//该节点能够达到的价值上界 
    int selected;   //该节点是否属于左节点(用于最终构造最优解) 
    int level;  //该节点是第几个物品的选择 
    int index;
    ItemNode parent; //该节点的父节点 
    public int compareTo(ItemNode node){  
        if(this.upprofit<node.upprofit)  
            return 1;  
        else if(this.upprofit == node.upprofit)  
            return 0;  
        else  
            return -1;  
    }  
}  

定义算法对象

public class AlgorithmFor01Bag {  
    int n = 5;
    int capacity = 10;
    int[] weight = { 2, 6, 4, 1, 5 };
    double[] value = { 6, 9, 6, 1, 4 };
    int maxValue ;
    int[] bestWay ;
    //存放下标为i的当前的value与weight在原来的数组中的位置
    private int[] originalplace;
    /** * 将原数组进行按照单位价值进行排序,初始化originalplace */
    private void sort(){
        ItemNode node;
        List<ItemNode> nodesList=new LinkedList<ItemNode>();
        for(int i=0;i<n;i++){
            node=new ItemNode();
            node.index=i
            node.value=value[i];
            node.weight=weight[i];
            nodesList.add(node);
        }
        Collections.sort(nodesList, new Comparator<ItemNode>() {
            @Override
            public int compare(ItemNode o1, ItemNode o2) {
                if(o1.value/o1.weight > o2.value/o2.weight)
                    return -1;
                else if(o1.value/o1.weight < o2.value/o2.weight)
                    return 1;
                return 0;
            }
        });
        int i = 0;
        originalplace = new int[value.length];
        for (ItemNode curnode : nodesList) {
            originalplace[i] = curnode.index;
            value[i] = curnode.value;
            weight[i] = curnode.weight;
            ++i;
        }

    }
 //实际上构建的是一颗子孙节点指向父节点,但是父节点不知道孩纸节点的二叉树,并且不断扩张该二叉树
    public void getMaxValue(double[] _value,int[] _weight,int limitopacity){  
        //为了当前操作不影响外面传进来的数组,复制传进的数组
        if(_value!=null&&_weight!=null){
            value=new double[_value.length];
            System.arraycopy(_value,0,value, 0,_value.length);
            weight=new int[_weight.length];
            System.arraycopy(_weight,0,weight, 0,_weight.length);
        }
        bestWay=new int[_value.length];
        capacity=limitopacity;
        maxValue = -1;
        n=_value.length;
        sort();
        PriorityQueue<ItemNode> pq = new PriorityQueue<ItemNode>();  
        //构造一个初始化节点,属于-1层 
        ItemNode initial = new ItemNode();  
        initial.level = 0;  //欺骗写法,为了是重用maxBound函数
        initial.weight=0;
        initial.value=0;
        initial.upprofit=maxBound(initial);
        initial.level=-1;
        pq.add(initial);  
        //*************core code begin********
        while(!pq.isEmpty()){  
            ItemNode fatherNode = pq.poll();

            // 当前节点是叶子节点,不用扩展,直接退出
            if (fatherNode.level == n - 1) {
                continue;
            }

            //试探左节点,做节点的原则是:容量可以容纳就加入该节点,加入改节点
            if (weight[fatherNode.level + 1] + fatherNode.weight <= capacity) {
                ItemNode leftNode = new ItemNode();
                leftNode.level = fatherNode.level + 1;
                leftNode.value = fatherNode.value + value[fatherNode.level + 1];
                leftNode.weight = weight[fatherNode.level + 1]
                        + fatherNode.weight;
                leftNode.upprofit = maxBound(leftNode);
                leftNode.parent = fatherNode;
                leftNode.selected = 1;

                //如果有最大值,记录最大值,可以自定义这个函数
                if (leftNode.value > maxValue) {
                    maxValue = (int) leftNode.value;
                    ItemNode tracknode = leftNode;
                    for (int i = leftNode.level; i >= 0; i--) {
                        bestWay[originalplace[i]] = tracknode.selected;
                        tracknode = tracknode.parent;// 往前遍历,到达上一层节点
                    }
                    for (int i = leftNode.level + 1; i < n; i++) {
                        bestWay[originalplace[i]] = 0;
                    }
                }

                if (leftNode.upprofit > maxValue) {
                    pq.add(leftNode);
                }
            }

            //试探右结点,右节点的原则是:不加入当前的节点,无论是否能够容纳
            ItemNode rightNode = new ItemNode();
            rightNode.level = fatherNode.level + 1;
            rightNode.value = fatherNode.value;
            rightNode.weight = fatherNode.weight;
            rightNode.upprofit=maxBound(rightNode);
            if (rightNode.upprofit > maxValue) {
                rightNode.parent = fatherNode;
                rightNode.selected = 0; // 表示当前节点不加入背包
                pq.add(rightNode);
            }
        }
        //*************core code end********
    }

    // 用于计算该节点的最高的贪心价值上界
    public double maxBound(ItemNode no) {
        double maxLeft = no.value;
        int leftWeight = capacity - no.weight;
        int templevel = no.level+1;
        // 尽力依照单位重量价值次序装剩余的物品
        while (templevel <= n - 1 && leftWeight > weight[templevel]) {
            leftWeight -= weight[templevel];
            maxLeft += value[templevel];
            templevel++;
        }
        // 不能装时,用下一个物品的单位重量价值折算到剩余空间。
        if (templevel <= n - 1) {
            maxLeft += value[templevel] / weight[templevel] * leftWeight;
        }
        return maxLeft;
    }

}  

测试用例:

public static void main(String[] args) {
        AlgorithmFor01Bag b = new AlgorithmFor01Bag();
// int[] weight = { 2, 6, 4, 1, 5 };
// double[] value = { 6, 9, 6, 1, 4 };
        int[] weight = { 2, 6, 4, 5, 1};
        double[] value = { 6, 9, 6, 4, 1 };
        b.getMaxValue(value, weight, 10);
        System.out.println("该背包能够取到的最大价值为:" + b.maxValue);
        System.out.println("取出的方法为:");
        for (int i : b.bestWay)
            System.out.print(i + " ");
    }
该背包能够取到的最大价值为:16
取出的方法为:
1  1  0  0  1  
    原文作者:分支限界法
    原文地址: https://blog.csdn.net/zhaoyubin2012/article/details/53135695
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞