分享-分支限界法(求最优装载)

本文介绍分支限界FIFO算法的基本概念、实现原理及应用案例。重点讲解了如何使用FIFO策略解决货箱装载问题,包括算法设计、Java代码实现等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 分支搜索算法

   (1) 引入
       用回溯算法解决问题时,是按照深度优先的策略在问题的状态空间中,尝试搜索可能的路径,不便于在搜索过程中对不同的解进行
比较,只能在搜索到所有解得情况下,才能通过比较确定哪个是最优解。这类问题更适合广度优先策略搜索,因为在扩展结点时,可以在
E-结点的各个子结点之间进行必要的比较,有选择的进行下一步扩展。这里的分支限界法就是一种较好的解决最优化问题的算法。

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

   (2) 深入
       分支搜索法是一种在问题解空间上进行搜索尝试的算法。所谓"分支"是采用广度优先的策略,依次搜索E-结点的所有分支,也就是
所有的相邻结点。和回溯法一样,在生成的结点中,抛弃那些不满足约束条件(或者说不可能导致最优可行解)的结点,其余结点加入到活结
点表。然后从表中选择下一个结点作为下一个E-结点,继续搜索。

   (3) 分类
       选择下一个E-结点的方式不同,会出现几种不同的分支搜索方式。

       1> FIFO 搜索
          先进先出(FIFO)搜索算法要依赖"队列"做基本的数据结构。一开始,根结点是唯一的活结点,根结点入队。
从活结点队列中取出根结点后,作为当前扩展结点。对当前扩展结点,先从左到右地产生它的所有儿子,用约束条件检查,
把所有满足约束条件的儿子加入到活结点队列中。再从活结点表中取出队首结点(队中最先进来的结点)为当前扩展结点,
......,直到找到一个解或活结点队列为空为止。

       2> LIFO搜索
          后进先出(LIFO)搜索算法要依赖"栈"做基本的数据结构。一开始,根结点入栈。从栈中弹出一个结点为当前扩展结点。对当前
扩展结点,先从左到右地产生它的所有儿子,用约束条件检查,把所有满足约束函数的儿子入栈,再从栈中弹出一个结点(栈中最后进来
的结点)为当前扩展结点,......,直到找到一个解或栈空为止。

       3> 优先队列式搜索
          为了加速搜索的进程,应采用有效的方式选择E-结点进行扩展。优先队列式搜索,对每一个结点计算一个优先级,并根据这些
优先级,从当前活结点表中优先选择一个优先级最高(最有利)的结点作为扩展结点,使得搜索朝着解空间树上最优解的分支推进,以便尽
快找到一个最优解。

2. 用FIFO分支搜索算法求解最优解

   (1) 问题描述:
       有两艘船和需要装运的n个货箱,第一艘船的载重量是c1,第二艘船的载重量是c2,wi是货箱的质量,且w1+w2+...+wn <= c1+c2.
希望确定是否有一种可将所有n个货箱全部装船的方法。若有的话,找出该方法。

   (2) 举例描述:
       当n=3,c1=c2=50,w=[10,40,40]时,可将货箱1、2装到第一艘船上,货箱3装到第二艘船上。
       但是如果w=[20,40,40],则无法将货箱全部装船。由此可知问题可能有解,可能无解,也可能有多解。
  
   (3) 问题分析
       虽然是关于两艘船的问题,其实只讨论一艘船的最大装载问题即可。因为当第一艘船的最大装载为bestw时,
若w1+w2+...+wn-bestw <= c2,则可以确定一种解,否则问题就无解。这样的问题转化为第一艘船的最大装载问题。

   (4) 算法设计
       转化为一艘船的最优化问题后, 问题的解空间为一个子集树。也就是算法要考虑所有物品取、舍情况的组合,
n个物品的取舍组合共有2的n次方个分支。

       1> 和回溯算法的思想一样,用FIFO分支搜索所有的分支,并记录已搜索分支的最优解,搜索完子集树也就找出了问题的解。
  
       2> 要想求出最优解,必须搜索到叶结点,所以要记录数的层次。当层次为n+1时,搜索完全部叶结点,算法结束。

       3> 分支搜索过程中活结点的"层"是需要标识的,否则入队后无法识别结点所在的层。每处理完一层让"-1"入队,以此来标识
          "层",并用变量i来记录当前层。

       4> 每个活结点要记录当前船的装载量。

3. Java代码:

Java代码 复制代码
  1. package boke.branchlimit;   
  2.   
  3. /**  
  4.  * 分支限界FIFO  
  5.  *   
  6.  * @since jdk1.6  
  7.  * @author 毛正吉  
  8.  * @version 1.0  
  9.  * @date 2010.05.25  
  10.  *   
  11.  */  
  12. public class BranchLimitFIFOSearch {   
  13.   
  14.     /**  
  15.      * @param args  
  16.      */  
  17.     public static void main(String[] args) {   
  18.         // n个货箱   
  19.         int n = 3;   
  20.         // 第一艘船的载重量   
  21.         float c1 = 50;   
  22.         // 第二艘船的载重量   
  23.         float c2 = 50;   
  24.         // 货箱质量数组   
  25.         float[] w = { 0104040 };   
  26.   
  27.         // 测试例子   
  28.         BranchLimitFIFOSearch bfis = new BranchLimitFIFOSearch(n, c1, c2, w);   
  29.         // 所有货箱的重量之和   
  30.         float s = bfis.getS();   
  31.         if (s <= c1 || s <= c2) {   
  32.             System.out.println("need only one ship!");   
  33.         }   
  34.         if (s > c1 + c2) {   
  35.             System.out.println("no solution!");   
  36.             return;   
  37.         }   
  38.   
  39.         bfis.maxLoading(c1);   
  40.   
  41.         float bestw = bfis.getBestw();   
  42.   
  43.         if (s - bestw <= c2) {   
  44.             System.out.println("The first ship loading " + bestw);   
  45.             System.out.println("The second ship loading " + (s - bestw));   
  46.         } else {   
  47.             System.out.println("no solution!");   
  48.         }   
  49.   
  50.     }   
  51.   
  52.     private int n; // n个货箱   
  53.     private float c1; // 第一艘船的载重量   
  54.     private float c2; // 第二艘船的载重量   
  55.     private float bestw; // 第一艘船的最大装载   
  56.     private float ew = 0// 当前船的装载量   
  57.     private float[] w; // 货箱质量数组   
  58.     private float s = 0// 所有货箱的重量之和   
  59.     private MyQueue mq = new MyQueue(); // FIFO队列   
  60.   
  61.     /**  
  62.      * 构造方法  
  63.      *   
  64.      * @param _n  
  65.      * @param _c1  
  66.      * @param _c2  
  67.      * @param _w  
  68.      */  
  69.     public BranchLimitFIFOSearch(int _n, float _c1, float _c2, float[] _w) {   
  70.         n = _n;   
  71.         c1 = _c1;   
  72.         c2 = _c2;   
  73.         w = _w;   
  74.   
  75.         for (float f : _w) {   
  76.             s += f;   
  77.         }   
  78.     }   
  79.   
  80.     /**  
  81.      * 最优装载值  
  82.      *   
  83.      * @param c  
  84.      */  
  85.     public float maxLoading(float c) {   
  86.         mq.put(new Float(-1)); // 初始化结点队列,标记分层   
  87.         int i = 1// E-结点的层   
  88.         ew = 0// 当前船的装载量   
  89.         bestw = 0// 目前的最优值   
  90.   
  91.         while (!mq.empty()) { // 搜索子集空间树   
  92.             if (ew + w[i] <= c) { // 检查E-结点的左孩子,货箱i是否可以装载   
  93.                 addLiveNode(ew + w[i], i); // 货箱i可以装载   
  94.             }   
  95.   
  96.             addLiveNode(ew, i); // 右孩子总是可行的,不装载货物i   
  97.   
  98.             ew = (Float) mq.get(); // 取下一个结点   
  99.   
  100.             if (ew == -1) { // 到达层的尾部   
  101.                 if (mq.empty()) {   
  102.                     return bestw;   
  103.                 }   
  104.                 mq.put(new Float(-1));   
  105.                 ew = (Float) mq.get(); // 取下一个结点   
  106.                 i++; // ew的层   
  107.             }   
  108.         }   
  109.         return bestw;   
  110.     }   
  111.   
  112.     /**  
  113.      * 入队  
  114.      *   
  115.      * @param wt  
  116.      * @param i  
  117.      */  
  118.     public void addLiveNode(float wt, int i) {   
  119.         if (i == n) { // 是叶子   
  120.             if (wt > bestw) {   
  121.                 bestw = wt;   
  122.             }   
  123.         } else { // 不是叶子   
  124.             mq.put(new Float(wt));   
  125.         }   
  126.     }   
  127.   
  128.     public int getN() {   
  129.         return n;   
  130.     }   
  131.   
  132.     public void setN(int n) {   
  133.         this.n = n;   
  134.     }   
  135.   
  136.     public float getC1() {   
  137.         return c1;   
  138.     }   
  139.   
  140.     public void setC1(float c1) {   
  141.         this.c1 = c1;   
  142.     }   
  143.   
  144.     public float getC2() {   
  145.         return c2;   
  146.     }   
  147.   
  148.     public void setC2(float c2) {   
  149.         this.c2 = c2;   
  150.     }   
  151.   
  152.     public float getBestw() {   
  153.         return bestw;   
  154.     }   
  155.   
  156.     public void setBestw(float bestw) {   
  157.         this.bestw = bestw;   
  158.     }   
  159.   
  160.     public float getEw() {   
  161.         return ew;   
  162.     }   
  163.   
  164.     public void setEw(float ew) {   
  165.         this.ew = ew;   
  166.     }   
  167.   
  168.     public float getS() {   
  169.         return s;   
  170.     }   
  171.   
  172.     public void setS(float s) {   
  173.         this.s = s;   
  174.     }   
  175.   
  176. }   
  177.   
  178. ------------------------------------------   
  179.   
  180. package boke.branchlimit;   
  181.   
  182. import java.util.LinkedList;   
  183.   
  184. /**  
  185.  * 自定义队列  
  186.  *   
  187.  * @since jdk1.6  
  188.  * @author 毛正吉  
  189.  * @version 1.0  
  190.  * @date 2010.05.25  
  191.  *   
  192.  */  
  193. public class MyQueue {   
  194.     private LinkedList ll = new LinkedList();   
  195.   
  196.     /**  
  197.      * 入队  
  198.      *   
  199.      * @param o  
  200.      */  
  201.     public void put(Object o) {   
  202.         ll.addLast(o);   
  203.     }   
  204.   
  205.     /**  
  206.      * 出队  
  207.      *   
  208.      * @return  
  209.      */  
  210.     public Object get() {   
  211.         return ll.removeFirst();   
  212.     }   
  213.   
  214.     /**  
  215.      * 队是否为空  
  216.      *   
  217.      * @return  
  218.      */  
  219.     public boolean empty() {   
  220.         return ll.isEmpty();   
  221.     }   
  222. }  
package boke.branchlimit;

/**
 * 分支限界FIFO
 * 
 * @since jdk1.6
 * @author 毛正吉
 * @version 1.0
 * @date 2010.05.25
 * 
 */
public class BranchLimitFIFOSearch {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// n个货箱
		int n = 3;
		// 第一艘船的载重量
		float c1 = 50;
		// 第二艘船的载重量
		float c2 = 50;
		// 货箱质量数组
		float[] w = { 0, 10, 40, 40 };

		// 测试例子
		BranchLimitFIFOSearch bfis = new BranchLimitFIFOSearch(n, c1, c2, w);
		// 所有货箱的重量之和
		float s = bfis.getS();
		if (s <= c1 || s <= c2) {
			System.out.println("need only one ship!");
		}
		if (s > c1 + c2) {
			System.out.println("no solution!");
			return;
		}

		bfis.maxLoading(c1);

		float bestw = bfis.getBestw();

		if (s - bestw <= c2) {
			System.out.println("The first ship loading " + bestw);
			System.out.println("The second ship loading " + (s - bestw));
		} else {
			System.out.println("no solution!");
		}

	}

	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队列

	/**
	 * 构造方法
	 * 
	 * @param _n
	 * @param _c1
	 * @param _c2
	 * @param _w
	 */
	public BranchLimitFIFOSearch(int _n, float _c1, float _c2, float[] _w) {
		n = _n;
		c1 = _c1;
		c2 = _c2;
		w = _w;

		for (float f : _w) {
			s += f;
		}
	}

	/**
	 * 最优装载值
	 * 
	 * @param c
	 */
	public float maxLoading(float c) {
		mq.put(new Float(-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));
				ew = (Float) mq.get(); // 取下一个结点
				i++; // ew的层
			}
		}
		return bestw;
	}

	/**
	 * 入队
	 * 
	 * @param wt
	 * @param i
	 */
	public void addLiveNode(float wt, int i) {
		if (i == n) { // 是叶子
			if (wt > bestw) {
				bestw = wt;
			}
		} 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;
	}

}

------------------------------------------

package boke.branchlimit;

import java.util.LinkedList;

/**
 * 自定义队列
 * 
 * @since jdk1.6
 * @author 毛正吉
 * @version 1.0
 * @date 2010.05.25
 * 
 */
public class MyQueue {
	private LinkedList ll = new LinkedList();

	/**
	 * 入队
	 * 
	 * @param o
	 */
	public void put(Object o) {
		ll.addLast(o);
	}

	/**
	 * 出队
	 * 
	 * @return
	 */
	public Object get() {
		return ll.removeFirst();
	}

	/**
	 * 队是否为空
	 * 
	 * @return
	 */
	public boolean empty() {
		return ll.isEmpty();
	}
}




#include #include #include #include using namespace std; ifstream infile; ofstream outfile; class Node { friend int func(int*, int, int, int*); public: int ID; double weight;//物品的重量 }; bool comp1(Node a, Node b) //定义比较规则 { return a.weight > b.weight; } class Load; class bbnode; class Current { friend Load; friend struct Comp2; private: int upweight;//重量上界 int weight;//结点相应的重量 int level;//活结点在子集树中所处的层次 bbnode* ptr;//指向活结点在子集树中相应结点的指针 }; struct Comp2 { bool operator () (Current *x, Current *y) { return x->upweightupweight; } }; class Load { friend int func(int*, int, int, int*); public: int Max0(); private: priority_queue<Current*, vector, Comp2>H;//利用优先队列(最大堆)储存 int limit(int i); void AddLiveNode(int up, int cw, bool ch, int level); bbnode *P;//指向扩展结点的指针 int c;//背包的容量 int n;//物品的数目 int *w;//重量数组 int cw;//当前装载量 int *bestx;//最优解方案数组 }; class bbnode { friend Load; friend int func( int*, int, int, int*); bbnode* parent; bool lchild; }; //结点中有双亲指针以及左儿子标志 int Load::limit(int i) //计算结点所相应重量的上界 { int left,a; left= c - cw;//剩余容量 a = cw; //b是重量上界,初始值为已经得到的重量 while (i <= n && w[i] parent = P; b->lchild = ch; Current* N = new Current; N->upweight = up; N->weight = cw; N->level = level; N->ptr = b; H.push(N); } int Load::Max0() { int i = 1; P = 0; cw = 0; int bestw = 0; int up = limit(1); while (i != n + 1) { int wt = cw + w[i]; //检查当前扩展结点的左儿子结点 if (wt bestw) bestw =wt; AddLiveNode(up,wt, true, i + 1); } up = limit(i + 1); //检查当前扩展结点的右儿子结点 if (up >= bestw)//如果右儿子可行 { AddLiveNode(up,cw, false, i + 1); } Current* N = H.top(); //取队头元素 H.pop(); P = N->ptr; cw = N->weight; up = N->upweight; i = N->level; } bestx = new int[n + 1]; for (int j = n; j > 0; --j) { bestx[j] = P->lchild; P = P->parent; } return cw; } int func(int *w, int c, int n, int *bestx) //调用Max0函数对子集树的优先队列式进行分支限界搜索 { int W = 0; //初始化装载的总质量为0 Node* Q = new Node[n]; for (int i = 0; i < n; ++i) { Q[i].ID = i + 1; Q[i].weight = w[i+1]; W += w[i+1]; } if (W <= c)//如果足够装,全部装入 return W; sort(Q, Q + n, comp1); //首先,将各物品按照重量从大到小进行排序; Load K; K.w = new int[n + 1]; for (int j = 0; j < n; j++) K.w[j + 1] = w[Q[j].ID]; K.cw = 0; K.c = c; K.n = n; int bestp = K.Max0(); for (int k = 0; k < n; k++) { bestx[Q[k].ID] = K.bestx[k + 1]; } delete []Q; delete []K.w; delete []K.bestx; return bestp; } int main() { int*w,*Final; int c,n,i,best; infile.open("input.txt",ios::in); if(!infile) { cerr<<"open error"<>c; infile>>n; w=new int[n+1]; for(i=1;i>w[i]; infile.close(); Final = new int[n+1]; best = func( w, c, n, Final); outfile.open("output.txt",ios::out); if(!outfile) { cerr<<"open error"<<endl; exit(1); } outfile << best << endl; for (int i = 1; i <= n; ++i) { outfile<<Final[i]<<" "; } outfile.close(); return 0; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值