分支界限法类似回溯法,也是在问题的解空间上搜索问题解的算法,其求解目标是找出满足约束条件的一个解(回溯是找出所有的解)或是在满足条件的解中找出最优解。 

搜索策略:在扩展结点处,先生成其所有的儿子节点(分支),然后再从当前的活结点表中(根据每一活结点计算出的函数值)选择最有利的结点作为下一个扩展结点。

从活结点表中选择下一扩展结点的不同方式导致不同的分支界限法:

1、队列式(FIFO)分支界限法

2、优先队列式分支界限法

基本思想:以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树。

每个活结点都只有一次机会成为扩展结点,活结点一旦成为扩展结点,就一次性产生其所有儿子结点。在这些儿子结点中,导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被加入活结点表中,重复上述结点扩展过程,直至找出所需的解。

分支界限法是由"分支"策略和"限界"策略两部分组成。"分支"策略体现在对问题空间是按照广度优先策略进行搜索的;"限界"策略是为了加速搜索速度而采用启发信息剪枝的策略。


装载问题

 有两艘船和需要装运的n个货箱,第一艘船的载重量是c1,第二艘船的载重量是c2,wi是货箱的质量,且w1+w2+...+wn <= c1+c2. 

希望确定是否有一种可将所有n个货箱全部装船的方法。若有的话,找出该方法。

容易证明:如果一个给定装载问题有解,则采用下面的策略可得到最优装载方案。 

   (1)首先将第一艘轮船尽可能装满;

   (2)将剩余的集装箱装上第二艘轮船

该问题就转化为第一艘船的最大装载问题

import java.util.*;

/*FIFO分支搜索算法求解最优解*/
public class BranchLimitFIFOSearch {  
    public static void main(String[] args) {    
        int n = 3;     // n个货箱          
        float c1 = 50;  // 第一艘船的载重量         
        float c2 = 50;  // 第二艘船的载重量   
        float[] w = { 0, 10, 40, 40 };      // 货箱质量数组   
        // 测试例子  
        BranchLimitFIFOSearch bfis = new BranchLimitFIFOSearch(n, c1, c2, w);  
         
        float s = bfis.getS();  // s为所有货箱的重量之和 
        if (s <= c1 || s <= c2) {  
            System.out.println("只需要一艘船!");  
        }  
        if (s > c1 + c2) {  
            System.out.println("无解!");  
            return;  
        }  
        bfis.maxLoading(c1);  
        float bestw = bfis.getBestw();   
        if (s - bestw <= c2) { 
            System.out.println("第一艘船装载" + bestw);  
            System.out.println("第二艘船装载 " + (s - bestw));  
        } else {  
            System.out.println("无解!");  
        }   
    }   
    private int n; // n个货箱  
    private float c1; // 第一艘船的载重量  
    private float c2; // 第二艘船的载重量  
    private float bestw; // 第一艘船的最大装载量 
    private float ew = 0; // 当前船的装载量  
    private float[] w; // 货箱质量数组  
    private float s = 0; // 所有货箱的重量之和  
    private MyQueue mq = new MyQueue(); // FIFO队列  
    
    //构造方法      
    public BranchLimitFIFOSearch(int _n, float _c1, float _c2, float[] _w) {  
        n = _n;  
        c1 = _c1;  
        c2 = _c2;  
        w = _w;  
  
        for (float f : _w) {  
            s += f;  
        }  
    }   
  
    //最优装载值   
    public float maxLoading(float c) {  
        mq.put(new Float(-1)); // 初始化结点队列,"-1"入队标记分层  
        int i = 1; // E-结点的层  
        ew = 0; // 当前船的装载量  
        bestw = 0; // 目前的最优值  
  
        while (!mq.empty()) { // 搜索子集空间树  
            if (ew + w[i] <= c) { // 检查E-结点的左孩子,货箱i是否可以装载  
                addLiveNode(ew + w[i], i); // 货箱i可以装载  
            }   
            addLiveNode(ew, i); // 右孩子总是可行的(不需要检查),不装载货物i    
            ew = (Float) mq.get(); // 取下一个结点  
  
            if (ew == -1) { // 到达层的尾部  
                if (mq.empty()) {  
                    return bestw;  
                } 
                mq.put(new Float(-1));//每处理完一层让"-1"入队,以此来标识"层"  
                ew = (Float) mq.get(); // 取下一个结点  
                i++; // ew的层 (当层次为n+1时,搜索完全部叶结点,算法结束) 
            }  
        }  
        return bestw;  
    }  
  
    //添加活结点(wt:当前装载量,i:当前层数)
    public void addLiveNode(float wt, int i) {  
        if (i == n) { // 可行叶结点 
            if (wt > bestw) {  
                bestw = wt;  //当前解由于当前最优解,更新bestw
            }  
        } else { // 非叶结点  
            mq.put(new Float(wt));  
        }  
    }  
  
    public int getN() {  
        return n;  
    }  
  
    public void setN(int n) {  
        this.n = n;  
    }  
  
    public float getC1() {  
        return c1;  
    }  
  
    public void setC1(float c1) {  
        this.c1 = c1;  
    }  
  
    public float getC2() {  
        return c2;  
    }  
  
    public void setC2(float c2) {  
        this.c2 = c2;  
    }  
  
    public float getBestw() {  
        return bestw;  
    }  
  
    public void setBestw(float bestw) {  
        this.bestw = bestw;  
    }  
  
    public float getEw() {  
        return ew;  
    }  
  
    public void setEw(float ew) {  
        this.ew = ew;  
    }  
  
    public float getS() {  
        return s;  
    }  
  
    public void setS(float s) {  
        this.s = s;  
    }  
  
//自定义队列
public class MyQueue {  
    private LinkedList list = new LinkedList();  
  
   //入队   
    public void put(Object o) {  
        list.addLast(o);  //链表末尾增加新元素
    }  
      
   // 出队     
    public Object get() {  
        return list.removeFirst();  
    }  
    
     //队是否为空     
    public boolean empty() {  
        return list.isEmpty();  
    }  
}  
}

分支界限法(BFS)_最大装载