分支限界

假如想象 提交于 2020-01-03 20:04:10

分支限界的基本思想

分支限界和回溯法都是在解空间树中进行搜索,但是他们的搜索方式不同,对于回溯法,他是从根节点开始以深度优先的策略进行搜索;而分支限界则是从根节点开始,以宽度优先为的方式进行搜索

分支限界通常用来找问题的一个最优解,而回溯法用来找所有的可行解

两种分支限界法

队列式(FIFO):按照队列先进先出原则选取下一个结点为扩展结点

优先级队列式:按照优先队列规定的优先级选取优先级最高的结点成为当前扩展结点

0-1背包问题

商品类记录没一件物品的价值(price)与重量(weight),还有单位价值(bw = price/weight)

class Produce{
    int id=0;
    int price;
    int weight;
    int bw;//物品的单位价值
    Produce(int price,int weight,int id)
    {
        this.id=id;
        this.price=price;
        this.weight=weight;
        bw=price/weight;
    }
}

背包类,记录背包的状态,最初什么也不装的背包是一种状态,装了某一个物品和不装该物品又是不同的状态

//背包类,用来记录当前的背包状态
class Bag{
    int totalPrice;//背包内的总价值
    int remindWeight;//背包内的剩余空间
    int maxPrice;//最大价值
    int maxWeight;//最大承载重量
    int i;//当前商品的序列值
    boolean boon = false;//是否爆炸
    String stute;

    Bag fatherBag;//前一个背包的状态。装入一个商品或不装入一个商品为一个背包状态

    //构造函数,初始的背包,什么也没放
    Bag(int maxWeight){
        this.maxWeight = maxWeight;
        fatherBag = null;
        totalPrice = 0;
        remindWeight = maxWeight;
        this.i = -1;
        stute="";
    }

    //不放入物品的背包状态 即右儿子节点
    Bag(Bag fatherBag, int i){
        totalPrice = fatherBag.totalPrice;
        remindWeight = fatherBag.remindWeight;
        maxPrice = fatherBag.totalPrice;
        maxWeight = fatherBag.maxWeight;
        this.i = i;
        this.fatherBag = fatherBag;
        stute=fatherBag.stute+"0";
    }

    //放入物品的背包状态 即左儿子节点
    Bag(Bag fatherBag,Produce produce, int i){
        totalPrice = fatherBag.totalPrice+produce.price;
        remindWeight = fatherBag.remindWeight - produce.weight;
        maxPrice = fatherBag.totalPrice + produce.price;
        if(remindWeight < 0){
            boon = true;//装不下
        }
        maxWeight = fatherBag.maxWeight;
        this.i = i;
        this.fatherBag = fatherBag;
        stute=fatherBag.stute+"1";
    }
}

商品链表,存放商品

List<Produce> list = new ArrayList<>();//商品列表

使用优先级队列来实现分支限界,优先级队列如下,优先级是这个状态下背包所能装的最大价值所决定的(maxPrice)

  //优先级队列,按照背包的最大价值为优先级存放背包状态
    PriorityQueue<Bag> heap = new PriorityQueue<Bag>(
            (a,b)->{return (b.maxPrice - a.maxPrice) == 0?b.remindWeight-a.remindWeight:b.maxPrice-a.maxPrice;}
    );

maxPrice使用getUpPrice(Bag tempBag, List list)函数来求,在当前背包状态对于后面还没装的物品,不断放入直到放不下,对于那个放不下的物品放入其最大能放入比例部分

//获取最大价值
    public void getUpPrice(Bag tempBag, List<Produce> list){
        int remind = tempBag.remindWeight;//剩余空间
        for(int i = tempBag.i + 1; i<list.size(); i++){
            int tempWeight = remind - list.get(i).weight;
            if(tempWeight >= 0){//装得下
                remind = tempWeight;
                tempBag.maxPrice+=list.get(i).price;
            }else {//装不下,则装入能装入比例
                tempBag.maxPrice += list.get(i).bw*remind;
                break;
            }
        }
    }

分支限界法是一次性生成弹出节点的左儿子节点和右儿子节点,一个背包状态为一个节点,并把生成的节点按优先级放入优先级队列

//初始化商品列表
        for(int i = 0; i<price.length; i++){
            Produce produce=new Produce(price[i],weight[i],i);
            list.add(produce);
        }
        //商品按照价值从大到小排序
        Collections.sort(list,(a,b)->b.bw-a.bw);

        Bag root = new Bag(maxWeight);//什么都没放的背包
        getUpPrice(root, list);
        heap.add(root);
        while (!heap.isEmpty()){
            Bag temp = heap.poll();
            //到达叶子节点
            if(temp.i>=list.size()-1){
                printAnswer(temp);
                break;
            }

            //不放入,右儿子节点
            Bag notPut = new Bag(temp, temp.i+1);
            if(!notPut.boon){//剪枝
                getUpPrice(notPut, list);
                heap.add(notPut);
            }

            //放入,左儿子节点
            Bag putIn=new Bag(temp,list.get(temp.i+1),temp.i+1);

            if(!putIn.boon){//剪枝
                getUpPrice(putIn, list);
                heap.add(putIn);
            }

        }

完整代码如下:

import java.util.*;

public class BagPro {
    int[] price = {10,14,20,9}; //每个东西的价格
    int[] weight = {5,7,10,3};  //每个东西的重量
    int maxWeight = 19;               //最多存储重量,背包容量
    List<Produce> list = new ArrayList<>();//商品列表
    int maxValue = 0;

    //优先级队列,按照背包的最大价值为优先级存放背包状态
    PriorityQueue<Bag> heap = new PriorityQueue<Bag>(
            (a,b)->{return (b.maxPrice - a.maxPrice) == 0?b.remindWeight-a.remindWeight:b.maxPrice-a.maxPrice;}
    );


    BagPro(){
        //初始化商品列表
        for(int i = 0; i<price.length; i++){
            Produce produce=new Produce(price[i],weight[i],i);
            list.add(produce);
        }
        //商品按照价值从大到小排序
        Collections.sort(list,(a,b)->b.bw-a.bw);

        Bag root = new Bag(maxWeight);//什么都没放的背包
        getUpPrice(root, list);
        heap.add(root);
        while (!heap.isEmpty()){
            Bag temp = heap.poll();
            //到达叶子节点
            if(temp.i>=list.size()-1){
                printAnswer(temp);
                System.out.println("最大价值为"+maxValue);
                break;
            }

            //不放入,右儿子节点
            Bag notPut = new Bag(temp, temp.i+1);
            if(!notPut.boon){//剪枝
                getUpPrice(notPut, list);
                heap.add(notPut);
            }

            //放入,左儿子节点
            Bag putIn=new Bag(temp,list.get(temp.i+1),temp.i+1);

            if(!putIn.boon){
                getUpPrice(putIn, list);
                heap.add(putIn);
            }

        }
    }

    //获取最大价值
    public void getUpPrice(Bag tempBag, List<Produce> list){
        int remind = tempBag.remindWeight;//剩余空间
        for(int i = tempBag.i + 1; i<list.size(); i++){
            int tempWeight = remind - list.get(i).weight;
            if(tempWeight >= 0){//装得下
                remind = tempWeight;
                tempBag.maxPrice+=list.get(i).price;
            }else {//装不下,则装入能装入比例
                tempBag.maxPrice += list.get(i).bw*remind;
                break;
            }
        }
    }

    //打印结果
    private void printAnswer(Bag temp)
    {
        for(int i=0;i<list.size();i++)
        {
            if(temp.stute.charAt(i)=='1')
                maxValue+=list.get(i).price;
                System.out.println("放入价值为"+list.get(i).price+",重量为 "+list.get(i).weight+"的商品");
        }
    }

    public static void main(String[] args) {
        BagPro test = new BagPro();
    }

}

//背包类,用来记录当前的背包状态
class Bag{
    int totalPrice;//背包内的总价值
    int remindWeight;//背包内的剩余空间
    int maxPrice;//最大价值
    int maxWeight;//最大承载重量
    int i;//当前商品的序列值
    boolean boon = false;//是否爆炸
    String stute;

    Bag fatherBag;//前一个背包的状态。装入一个商品或不装入一个商品为一个背包状态

    //构造函数,初始的背包,什么也没放
    Bag(int maxWeight){
        this.maxWeight = maxWeight;
        fatherBag = null;
        totalPrice = 0;
        remindWeight = maxWeight;
        this.i = -1;
        stute="";
    }

    //不放入物品的背包状态 即右儿子节点
    Bag(Bag fatherBag, int i){
        totalPrice = fatherBag.totalPrice;
        remindWeight = fatherBag.remindWeight;
        maxPrice = fatherBag.totalPrice;
        maxWeight = fatherBag.maxWeight;
        this.i = i;
        this.fatherBag = fatherBag;
        stute=fatherBag.stute+"0";
    }

    //放入物品的背包状态 即左儿子节点
    Bag(Bag fatherBag,Produce produce, int i){
        totalPrice = fatherBag.totalPrice+produce.price;
        remindWeight = fatherBag.remindWeight - produce.weight;
        maxPrice = fatherBag.totalPrice + produce.price;
        if(remindWeight < 0){
            boon = true;//装不下
        }
        maxWeight = fatherBag.maxWeight;
        this.i = i;
        this.fatherBag = fatherBag;
        stute=fatherBag.stute+"1";
    }
}

class Produce{
    int id=0;
    int price;
    int weight;
    int bw;//物品的单位价值
    Produce(int price,int weight,int id)
    {
        this.id=id;
        this.price=price;
        this.weight=weight;
        bw=price/weight;
    }

}



旅行售货员问题

一个HeapNode类存放每个位置的节点信息

public static class HeapNode implements Comparable{
        int s;//当前节点编号
        float lcost;//子树费用下界
        float currentCost;//当前费用
        float rcost;//x[s:n-1]中顶点最小出边费用和
        int[] x;//需要进一步搜索的顶点是x[s+1:n-1]

        public HeapNode(float lcost, float currentCost, float rcost, int s, int[] x){
            this.lcost = lcost;
            this.currentCost = currentCost;
            this.rcost = rcost;
            this.s = s;
            this.x = x;
        }

        @Override
        public int compareTo(Object o) {
            if(lcost<((HeapNode)o).lcost){
                return -1;
            }else if(lcost==((HeapNode)o).lcost){
                return 0;
            }else {
                return 0;
            }
        }
    }

全部代码:

import java.util.Collections;
import java.util.LinkedList;

public class BBTSP {
    float[][] a;//图
    public BBTSP(float[][] a){
        this.a = a;
    }

    public static class HeapNode implements Comparable{
        int s;//当前节点编号
        float lcost;//子树费用下界
        float currentCost;//当前费用
        float rcost;//x[s:n-1]中顶点最小出边费用和
        int[] x;//需要进一步搜索的顶点是x[s+1:n-1]

        public HeapNode(float lcost, float currentCost, float rcost, int s, int[] x){
            this.lcost = lcost;
            this.currentCost = currentCost;
            this.rcost = rcost;
            this.s = s;
            this.x = x;
        }

        @Override
        public int compareTo(Object o) {
            if(lcost<((HeapNode)o).lcost){
                return -1;
            }else if(lcost==((HeapNode)o).lcost){
                return 0;
            }else {
                return 0;
            }
        }
    }

    public float bbTsp(int[] v){
        int n = v.length-1;
        //优先级队列
        LinkedList<HeapNode> heap = new LinkedList<HeapNode>();
        //节点i的最小出边费用
        float[] minOut = new float[n+1];
        float minSum = 0;//最小出边费用和
        //针对每个节点,找到最小出边
        for(int i = 1; i<=n; i++){
            float min = Float.MAX_VALUE;
            for(int j = 1; j<=n; j++){
                if(a[i][j] < Float.MAX_VALUE&&a[i][j]<min){
                    min = a[i][j];
                }
            }
            if(min == Float.MAX_VALUE){
                return Float.MAX_VALUE;
            }
            minOut[i] = min;
            minSum+=min;
        }

        //初始化
        int[] x = new int[n];
        for(int i=0;i<n;i++)
            x[i]=i+1;
        HeapNode enode = new HeapNode(0,0,minSum,0,x);
        float bestc = Float.MAX_VALUE;

        //搜索解空间树
        while (enode!=null && enode.s<n-1){//非叶节点
            x = enode.x;
            //当前扩展结点为叶子节点的父节点
            if(enode.s == n-2){
                if(a[x[n-2]][x[n-1]]!=-1&&a[x[n-1]][1]!=-1&&enode.currentCost+a[x[n-2]][x[n-1]]+a[x[n-1]][1]<bestc){
                    bestc = enode.currentCost+a[x[n-2]][x[n-1]]+a[x[n-1]][1];
                    enode.currentCost = bestc;
                    enode.lcost = bestc;
                    enode.s++;
                    heap.add(enode);
                    Collections.sort(heap);
                }
            }else {
                for(int i=enode.s+1;i<n;i++){
                    if(a[x[enode.s]][x[i]]!=-1){
                        //可行儿子结点
                        float cc=enode.currentCost+a[x[enode.s]][x[i]];
                        float rcost=enode.rcost-minOut[x[enode.s]];
                        float b=cc+rcost;//下界
                        if(b<bestc){
                            //子树可能含有最优解,结点插入最小堆
                            int[] xx=new int[n];
                            for(int j=0;j<n;j++)
                                xx[j]=x[j];
                            xx[enode.s+1]=x[i];
                            xx[i]=x[enode.s+1];
                            HeapNode node=new HeapNode(b,cc,rcost,enode.s+1,xx);
                            heap.add(node);
                            Collections.sort(heap);
                        }
                    }
                }

            }
            enode = heap.poll();
        }
        for(int i=0;i<n;i++)
            v[i+1]=x[i];
        return bestc;
    }
    public static void main(String[] args) {
        //int n=4;
        //float[][] a={{0,0,0,0,0},{0,-1,30,6,4},{0,30,-1,5,10},{0,6,5,-1,20},{0,4,10,20,-1}};//a下标从1开始,0用来凑数;-1表示不同,1表示连通
        int n=5;
        float[][] a={{0,0,0,0,0,0},{0,-1,5,-1,7,9},{0,5,-1,10,3,6},{0,-1,10,-1,8,-1},{0,7,3,8,-1,4},{0,9,6,-1,4,-1}};//a下标从1开始,0用来凑数;-1表示不同,1表示连通
        BBTSP b=new BBTSP(a);
        int []v=new int[n+1];
        System.out.println("最短回路长为:"+b.bbTsp(v));
        System.out.print("最短回路为:");
        for(int i=1;i<=n;i++){
            System.out.print(v[i]+" ");
        }
    }
}

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!