分支限界法实验(最优装载问题)
第6章 分支限界法(1-例子)

6.3 装载问题
3. 算法的改进
// 检查右儿子结点 // 检查左儿子结点 Type wt = Ew + w[i]; if (wt <= c) { // 可行结点 右儿子剪枝
if (Ew + r > bestw && i < n) Q.Add(Ew); // 可能含最优解 Q.Delete(Ew);// 取下一扩展结点 提前更新 bestw
5. 优先队列式分支限界法
解装载问题的优先队列式分支限界法用最大优先队列存 储活结点表。 活结点x在优先队列中的优先级定义为从根结点到结点x的 路径所相应的载重量再加上剩余集装箱的重量之和。 优先队列中优先级最大的活结点成为下一个扩展结点。 在优先队列式分支限界法中,一旦有一个叶结点成为当 前扩展结点,则可以断言该叶结点所相应的解即为最优解。 此时可终止算法。
while6.3 (true)装载问题 { // 检查左儿子结点 2. 队列式分支限界法 if (Ew + w[i] <= c) // x[i] = 1 EnQueue(Q, Ew + w[i], bestw, i, n); // 右儿子结点总是可行的 EnQueue(Q, Ew, bestw, i, n); // x[i] = 0 Q.Delete(Ew); // 取下一扩展结点 if (Ew == -1) { // 同层结点尾部 if (Q.IsEmpty()) return bestw; Q.Add(-1); // 同层结点尾部标志 Q.Delete(Ew); // 取下一扩展结点 i++; // 进入下一层 } }
if (wt > bestw) bestw = wt; // 加入活结点队列 if (i < n) Q.Add(wt); }
用分支限界算法解装载问题详解

用分支限界算法解装载问题详解一、实验目的1、理解分支限界法的概念,掌握分支限界法的基本要素。
2、掌握设计分支限界法的一般步骤,针对具体问题,能应用分支限界法求解二、实验内容1、问题描述:有一批共个集装箱要装上2艘载重量分别为C1和C2的轮船,其中集装箱i的重量为Wi,且w1+…+wn<= C1+ C2; 装载问题要求确定是否有一个合理的装载方案可将这个集装箱装上这2艘轮船。
如果有,找出一种装载方案。
2、数据输入:文件输入或键盘输入。
3、要求:1)完成上述问题的队列式分支限界法解决问题,时间为1 次课。
2)独立完成实验及实验报告。
三、实验步骤1、理解方法思想和问题要求。
2、采用编程语言实现题目要求。
3、上机输入和调试自己所写的程序。
4、附程序主要代码:#include <bits/stdc++、h>using namespace std;class MaxHeapQNode{public: MaxHeapQNode*parent; int lchild; int weight; int lev;};structcmp{ bool operator()(MaxHeapQNode *&a, MaxHeapQNode *&b)const { return a->weight < b->weight; }};int n;int c;int bestw;int w[100];int bestx[100];voidInPut(){ scanf("%d %d", &n, &c); for(int i =1; i <= n;++i)scanf("%d", &w[i]);}voidAddAliveNode(priority_queue<MaxHeapQNode *,vector<MaxHeapQNode *>, cmp> &q, MaxHeapQNode *E, int wt, int i, int ch){ MaxHeapQNode *p = new MaxHeapQNode; p->parent = E; p->lchild = ch; p->weight = wt; p->lev = i +1; q、push(p);}voidMaxLoading(){ priority_queue<MaxHeapQNode *,vector<MaxHeapQNode *>, cmp > q; // 大顶堆 //定义剩余重量数组r int r[n +1]; r[n] = 0; for(int j = n-j)r[j] = r[j +1] + w[j +1]; int i =1; MaxHeapQNode *E; int Ew = 0; while(i != n +1){ if(Ew + w[i] <= c){ AddAliveNode(q, E, Ew + w[i] + r[i], i,1); } AddAliveNode(q, E, Ew + r[i], i, 0); //取下一节点 E = q、top(); q、pop(); i = E->lev; Ew = E->weight1]; } bestw = Ew; for(int j = n; j > 0;j){ bestx[j] = E->lchild; E = E->parent; }}void OutPut(){ printf("最优装载量为 %d\n", bestw); printf("装载的物品为 \n"); for(int i =1; i <= n; ++i)if(bestx[i] ==1)printf("%d ", i);}int main(){ InPut(); MaxLoading(); OutPut();}5、实验结果:4、装载问题实验分析:1、将wt<=c和Ew+r>=bestw作为限界判定。
9——分支限界法

2 例9.1-限界
x3=1
x2=1 4
x1=1 2 x2=0 5
1
x1=0 3 x2=1 6
x2=0
x3=1 x3=0
x3=0 x3=1
x3=0 x3=1 x3=0
7
8
9
10
11 12
13
14
15
W={50,10,10},C1=60。 在此例中,结点3所在分支的所有子树中,装载货物的最 大可能是多少? 20。
20
2 例9.1-算法2 FIFO分支限界
AddLiveNode(folat wt,int i, QNode *E, int ch) { Qnode *b; if (i=n) //叶子 { if (wt>bestw) //目前的最优解 { bestE=E; bestx[n]=ch;} //bestx[n]取值为ch return; } b = new QNode; // 不是叶子, 添加到队列中 b->weight=wt; b->parent=E; b->LChild=ch; add (Q,b) ; }
3 7 14 15
分支搜索法是一种在问题解空间上进行搜索尝试的算法。 所谓“分支”是采用广度优先的策略,依次生成E-结点所 有分支,也就是所有的儿子结点。 和回溯法一样,可以在生成的结点中,抛弃那些不满足约 束条件的结点,其余结点加入活结点表。然后从表中选择 一个结点作为下一个E-结点。 选择下一个E-结点方式的不同导致几种分支搜索方式:
8
2 例9.1 装载问题
子集树
x3=1
x2=1 4
x1=1 2 x2=0 5
1
x1=0 3 x2=1 6
实验五、优先队列式分支限界法解装载问题

实验五优先队列式分支限界法解装载问题09电信实验班I09660118 徐振飞一、实验题目实现书本P201所描述的优先队列式分支限界法解装载问题二、实验目的(1)掌握并运用分支限界法基本思想(2)运用优先队列式分支限界法实现装载问题(3)比较队列式分支限界法和优先队列式分支限界法的优缺点三、实验内容和原理(1)实验内容有一批共n个集装箱要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为Wi,且∑=+≤niiccw121,要求确定是否有一个合理的装载方案可将这n个集装箱装上这2艘轮船。
如果有,请给出方案。
(2)实验原理解装载问题的优先队列式分支限界法用最大优先队列存储活结点表。
活结点x在优先队列中的优先级定义为从根结点到结点x的路径所相应的载重量再加上剩余集装箱的重量之和。
优先队列中优先级最大的活结点成为下一个扩展结点。
优先队列中活结点x的优先级为x.uweight。
以结点x为根的子树中所有结点相应的路径的载重量不超过x.uweight。
子集树中叶结点所相应的载重量与其优先级相同。
因此在优先队列式分支限界法中,一旦有一个叶结点成为当前扩展结点,则可以断言该叶结点所相应的解即为最优解,此时终止算法。
上述策略可以用两种不同方式来实现。
第一种方式在结点优先队列的每一个活结点中保存从解空间树的根结点到该活结点的路径,在算法确定了达到最优值的叶结点时,就在该叶结点处同时得到相应的最优解。
第二种方式在算法的搜索进程中保存当前已构造出的部分解空间树,在算法确定了达到最优值的叶结点时,就可以在解空间树中从该叶结点开始向根结点回溯,构造出相应的最优解。
在下面的算法中,采用第二种方式。
四、源程序import parator;import java.util.Iterator;import java.util.PriorityQueue;import java.util.Scanner;public class test5 {public void addLiveNode(PriorityQueue<HeapNode> H,bbnode E,int wt,boolean ch,int lev){bbnode b = new bbnode(E,ch);HeapNode N = new HeapNode(b, wt, lev);H.add(N);}public int maxLoading(int w[],int c,int n,boolean bestx[]){PriorityQueue<HeapNode> H = new PriorityQueue(1000,new comp());/*生成最大堆*/int[] r = new int[n+1];r[n] = 0;for(int j=n-1;j>0;j--){r[j] = r[j+1] + w[j+1];}int i = 1;bbnode E = new bbnode(null,false);int Ew = 0;while(i!=n+1){if(Ew+w[i]<=c){addLiveNode(H, E, Ew+w[i]+r[i], true, i+1);}addLiveNode(H, E, Ew+r[i], false, i+1);HeapNode N;N=H.poll();i = N.level;E = N.ptr;Ew = N.uweight - r[i-1];}//构造最优解for(int j=n;j>0;j--){bestx[j] = E.Lchild;E = E.parent;}return Ew;}public static void main(String[] args){System.out.println("请输入物品总数:");Scanner sc1 = new Scanner(System.in);int n = sc1.nextInt();int[] w = new int[n+1];System.out.println("请输入物品重量:");Scanner sc2 = new Scanner(System.in);for(int i=1;i<=n;i++){w[i] = sc2.nextInt();}System.out.println("请输入箱子重量:");Scanner sc3 = new Scanner(System.in);int c1 = sc3.nextInt();int c2 = sc3.nextInt();boolean[] bestx = new boolean[100];test5 t = new test5();//处理第一个箱子System.out.println("first:"+t.maxLoading(w, c1, n, bestx));System.out.print("可装重为:");int count = 0;for(int i=1;i<=n;i++){if(bestx[i]){count++;System.out.print(w[i]+" "); /*输出一个可行方案*/ }}System.out.println();/*处理第二个箱子*/int m = n - count;int[] ww = new int[m+1];int k = 1;for(int i=1;i<=n;i++){if(!bestx[i]){ww[k] = w[i];k++;bestx[i] = false;}}System.out.println();System.out.println("second:"+t.maxLoading(ww, c2, m, bestx));System.out.print("可装重为:");for(int i=1;i<=m;i++){if(bestx[i]){System.out.print(ww[i]+" "); /*输出一个可行方案*/ }}}}/*堆结点类*/class HeapNode{bbnode ptr;int uweight;int level;public HeapNode(){}public HeapNode(bbnode ptr,int uweight,int level){this.ptr = ptr;this.uweight = uweight;this.level = level;}public String toString(){return ""+this.uweight;}}class bbnode{bbnode parent;boolean Lchild;public bbnode(bbnode node,boolean ch){this.parent = node;this.Lchild = ch;}}//定义比较器类class comp implements Comparator<HeapNode>{@Overridepublic int compare(HeapNode o1, HeapNode o2) {int dif = o1.uweight-o2.uweight;if(dif>0){return -1;}else if(dif==0){return 0;}else{return 1;}}}五、实验结果和分析a.输入格式说明:(1)首先输入物品总数量(2)第二栏输入所有物品重量(3)第三栏输入2个箱子的重量b.输出格式说明:(1)首先输出first的字样,后面的数字表示第一个箱子所能装载的最大重量,紧接着的一行输出一种可以选择装载的方案(2)Second字样后面的数字表示第二个箱子所能装载的最大重量,紧接着的一行输出一种可行方案经过分析,上述结果正确。
算法——分支限界法(装载问题)

算法——分⽀限界法(装载问题)对⽐回溯法回溯法的求解⽬标是找出解空间中满⾜约束条件的所有解,想必之下,分⽀限界法的求解⽬标则是找出满⾜约束条件的⼀个解,或是满⾜约束条件的解中找出使某⼀⽬标函数值达到极⼤或极⼩的解,即在某种意义下的最优解。
另外还有⼀个⾮常⼤的不同点就是,回溯法以深度优先的⽅式搜索解空间,⽽分⽀界限法则以⼴度优先的⽅式或以最⼩耗费优先的⽅式搜索解空间。
分⽀限界法的搜索策略在当前节点(扩展节点)处,先⽣成其所有的⼉⼦节点(分⽀),然后再从当前的活节点(当前节点的⼦节点)表中选择下⼀个扩展节点。
为了有效地选择下⼀个扩展节点,加速搜索的进程,在每⼀个活节点处,计算⼀个函数值(限界),并根据函数值,从当前活节点表中选择⼀个最有利的节点作为扩展节点,使搜索朝着解空间上有最优解的分⽀推进,以便尽快地找出⼀个最优解。
分⽀限界法解决了⼤量离散最优化的问题。
选择⽅法1.队列式(FIFO)分⽀限界法队列式分⽀限界法将活节点表组织成⼀个队列,并将队列的先进先出原则选取下⼀个节点为当前扩展节点。
2.优先队列式分⽀限界法优先队列式分⽀限界法将活节点表组织成⼀个优先队列,并将优先队列中规定的节点优先级选取优先级最⾼的下⼀个节点成为当前扩展节点。
如果选择这种选择⽅式,往往将数据排成最⼤堆或者最⼩堆来实现。
例⼦:装载问题有⼀批共n个集装箱要装上2艘载重量分别为c1,c2的轮船,其中集装箱i的重量为wi,且要求确定是否有⼀个合理的装载⽅案可将这n个集装箱装上这2艘轮船。
可证明,采⽤如下策略可以得到⼀个最优装载⽅案:先尽可能的将第⼀艘船装满,其次将剩余的集装箱装到第⼆艘船上。
代码如下://分⽀限界法解装载问题//⼦函数,将当前活节点加⼊队列template<class Type>void EnQueue(Queue<Type> &Q, Type wt, Type &bestw, int i, int n){if(i == n) //可⾏叶结点{if(wt>bestw) bestw = wt ;}else Q.Add(wt) ; //⾮叶结点}//装载问题先尽量将第⼀艘船装满//队列式分⽀限界法,返回最优载重量template<class Type>Type MaxLoading(Type w[],Type c,int n){//初始化数据Queue<Type> Q; //保存活节点的队列Q.Add(-1); //-1的标志是标识分层int i=1; //i表⽰当前扩展节点所在的层数Type Ew=0; //Ew表⽰当前扩展节点的重量Type bestw=0; //bestw表⽰当前最优载重量//搜索⼦集空间树while(true){if(Ew+w[i]<=c) //检查左⼉⼦EnQueue(Q,Ew+w[i],bestw,i,n); //将左⼉⼦添加到队列//将右⼉⼦添加到队列即表⽰不将当前货物装载在第⼀艘船EnQueue(Q,Ew,bestw,i,n);Q.Delete(Ew); //取下⼀个节点为扩展节点并将重量保存在Ewif(Ew==-1) //检查是否到了同层结束{if(Q.IsEmpty()) return bestw; //遍历完毕,返回最优值Q.Add(-1); //添加分层标志Q.Delete(Ew); //删除分层标志,进⼊下⼀层i++;}}}算法MaxLoading的计算时间和空间复杂度为O(2^n).上述算法可以改进,设r为剩余集装箱的重量,当Ew+r<=bestw的时候,可以将右⼦树剪去。
分支限界法实现实验报告

队列式分支限界法将活节点表组织成一个队列,并将队列的先进先出原则选取下一个节点为当前扩展节点。
2.优先队列式分支限界法
优先队列式分支限界法将活节点表组织成一个优先队列,并将优先队列中规定的节点优先级选取优先级最高的下一个节点成为当前扩展节点。如果选择这种选择方式,往往将数据排成最大堆或者最小堆来实现。
for (j=0;j<City_Size;j++){
temp_x[j]=Best_Cost_Path[j];
}
temp_x[pNode->x[pNode->s+1]] = Best_Cost_Path[i];
temp_x[i] = Best_Cost_Path[pNode->s+1];
Node* pNextNode = new Node;
int edge2 = City_Graph[(pNode->x)[City_Size-1]][(pNode->x)[0]];
if(edge1 >= 0 && edge2 >= 0 && (pNode->cc+edge1+edge2) < Best_Cost){
Best_Cost = pNode->cc + edge1+edge2;
二、实验过程记录:
打开MicrosoftC++2008,键入代码进行编程:
源代码为:
#include <stdio.h>
#include "stdafx.h"
#include <istream>
分支与限界:货物装载问题

分支与限界:货物装载问题课程名称:**************院系:************************学生姓名:******学号:************专业班级:***************************** 指导教师:*******2013年12月27日分支与限界:货物装载问题摘要:在现实生活中,我们会经常遇见货物装载问题,如何解决货物装载问题,获取利润的最大化,花费最少的而得到更多的东西,是人们一直都要考虑的问题。
在广泛的解决问题中,人们一般采用分支限界算法解决这样的问题。
分支限界法是由分支策略和限界策略两部分组成的。
分枝定界法是一个用途十分广泛的算法,运用这种算法的技巧性很强,不同类型的问题解法也各不相同。
该算法在具体执行时,把全部可行的解空间不断分割为越来越小的子集(称为分支),并为每个子集内的解的值计算一个下界或上界(称为定界)。
在每次分支后,对凡是界限超出已知可行解值那些子集不再做进一步分支。
这样,解的许多子集(即搜索树上的许多结点)就可以不予考虑了,从而缩小了搜索范围。
该算法在具体执行时,把全部可行的解空间不断分割为越来越小的子集(称为分支),并为每个子集内的解的值计算一个下界或上界(称为定界)。
在每次分支后,对凡是界限超出已知可行解值那些子集不再做进一步分支。
这样,解的许多子集(即搜索树上的许多结点)就可以不予考虑了,从而缩小了搜索范围。
分支策略体现在对问题空间是按广度优先的策略进行搜索,限界策略是为了加速搜索速度而采用启发信息剪枝的策略。
在分支限界法,经常采用的是分支搜索法,分支搜索法是一种在问题空间上进行搜索尝试的算法。
所谓分支是采用广度优先的策略,依次搜索E-结点的所有的分支,也就是所有的相邻结点。
和回溯法一样,在生成的节点中,抛弃那些不满足的约束条件的的结点,其余结点加入活结点表。
在分支搜索的算法中,人们经常会采用FIFO搜索和优先队列式搜索。
分支限界法经典案例算法分析

3. 算法的改进
6.3 装载问题
// 检查左儿子结点 Type wt = Ew + w[i]; // 左儿子结点的重量 if (wt <= c) { // 可行结点 提前更新 if (wt > bestw) bestw = wt; bestw // 加入活结点队列 if (i < n) Q.Add(wt); } 右儿子剪枝 // 检查右儿子结点 if (Ew + r > bestw && i < n) Q.Add(Ew); // 可能含最优解 Q.Delete(Ew); // 取下一扩展结点
6.3 装载问题
1. 问题描述
有一批共个集装箱要装上2艘载重量分别为C1和C2的轮船, 其中集装箱i的重量为Wi,且 n
w
i 1
i
c1 c2
装载问题要求确定是否有一个合理的装载方案可将这个 集装箱装上这2艘轮船。如果有,找出一种装载方案。 容易证明:如果一个给定装载问题有解,则采用下面的 策略可得到最优装载方案。 (1)首先将第一艘轮船尽可能装满; (2)将剩余的集装箱装上第二艘轮船。
定义移动方向 的相对位移
设置边界的围 墙 for (int i = 0; i <= m+1; i++) grid[0][i] = grid[n+1][i] = 1; // 顶部和底部 for (int i = 0; i <= n+1; i++) grid[i][0] = grid[i][m+1] = 1; // 左翼和右翼
6.3 装载问题
将第一艘轮船尽可能装满等 价于选取全体集装箱的一个 子集,使该子集中集装箱重 量之和最接近。由此可知, 装载问题等价于以下特殊的 0-1背包问题。 例如:
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
算法分析与设计实验报告第八次附加实验
for(int i=1;i<n;i++)
cout<<bestx[i]<<",";
cout<<bestx[n]<<")"<<endl;
}
测试结果
当输入物品数量较少时:
当输入物品数量较一般时:
当输入物品数量较大时:
实验心得
最优装载就是求解载重量最好的载重方案,之前最优装载采用了贪心算法,这里使用的使分支限界法;其实在使用分支限界法进行了单源最短路径的
实现之后,在完成这个实验就显得更简单一点。
分支限界法的算法思想本身还
是很好理解的,但是在这个问题中由于涉及到了剪枝的问题,所以新加入了一
个0来判断一层是否结束,因为在一层结束的时候,代表该集装箱的情况已经
考虑完了,需要考虑下一个集装箱,这个时候就需要更新剩余集装箱的重量,
这会影响到一个右子树是否会被剪掉,因此要特别注意。
完整代码(分支限界法)
//分支限界法求最优装载
#include<iostream>
#include<time.h>
#include<iomanip>
#include<queue>
using namespace std;
class QNode
{
friend void Enqueue(queue<QNode *>&,int,int,int,int,QNode *,QNode *&,int *,bool);
friend void Maxloading(int *,int,int,int *);
private:
QNode *parent; //指向父节点的指针
bool LChild; //左儿子标志,用来表明自己是否为父节点的左儿子
int weight; //节点所相应的载重量
};
void Enqueue(queue<QNode *>&Q,int wt,int i,int n,int bestw,QNode
*E,QNode *&bestE,int bestx[],bool ch)
{
//将活节点加入到队列中
if(i==n) //到达叶子节点
{
if(wt==bestw) //确保当前解为最优解
{
bestE=E;
bestx[n]=ch;
}
return;
}
//当不为叶子节点时,加入到队列中,并更新载重、父节点等信息
QNode *b;
b=new QNode;
b->weight=wt;
b->parent=E;
b->LChild=ch;
Q.push(b);
}
void Maxloading(int w[],int c,int n,int bestx[]) //其中w[]为重量数组¦ { // c为船的总载重量,n为节点数
//初始化
queue <QNode *>Q; //活节点队列
Q.push(0); //将第一个节点加入队列中,并用0表示同层节点的尾部标志int i=1; //当前扩展节点的层数,此时为
int Ew=0; //扩展节点相应的载重量
int bestw=0; //当前最优载重量
int r=0; //剩余集装箱的重量
for(int j=2;j<=n;j++) //求得最初的剩余载重量
r+=w[j];
QNode *E =0; //当前扩展节点
QNode *bestE; //当前最优扩展节点
//搜索子集空间树
while(true)
{
//首先检查左儿子节点
int wt=Ew+w[i];
if(wt<=c) //左儿子节点为可行结点
{
if(wt>bestw)
bestw=wt;
Enqueue(Q,wt,i,n,bestw,E,bestE,bestx,true);//将左节点加入队列
}
//检查右儿子节点,利用上届函数
if(Ew+r>=bestw)
Enqueue(Q,Ew,i,n,bestw,E,bestE,bestx,false);//将右节点加入队列
E=Q.front(); //取出当前扩展节点
Q.pop();
if(!E) //到达同层的最末,此时需要更新剩余装箱载重量
{
if(Q.empty()) break; //此时队列为空
Q.push(0); //加入0,表示该层结束
E=Q.front();
Q.pop();
i++; //进入下一层
r-=w[i];//更新剩余集装箱的值
}
Ew=E->weight; //新扩展的节点对应的载重量
}
//构造当前最优解
for(int j=n-1;j>0;j--)
{
bestx[j]=bestE->LChild;
bestE=bestE->parent;
}
cout<<"最优载重量为:"<<bestw<<endl;
cout<<"最优载重方式:"<<"(";
for(int i=1;i<n;i++)
cout<<bestx[i]<<",";
cout<<bestx[n]<<")"<<endl;
}
int main()
{
int n;
cout<<"please input number of node:";
cin>>n;
int *w=new int[n+1];
int *bestx=new int[n+1];
cout<<"please input weight:"<<endl;
for(int i=1;i<=n;i++)
cin>>w[i];
int c;
cout<<"please input limit weight:";
cin>>c;
clock_t start,end,over;
start=clock();
end=clock();
over=end-start;
start=clock();
Maxloading(w,c,n,bestx);
cout<<"The time is "<<((double)(end-start-over)/CLK_TCK)<<endl;
system("pause");
return 0;
}。