分支限界法求解装载问题实验总结(114)

合集下载

用分支限界算法解装载问题详解

用分支限界算法解装载问题详解

用分支限界算法解装载问题详解一、实验目的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作为限界判定。

实验五、优先队列式分支限界法解装载问题

实验五、优先队列式分支限界法解装载问题

实验五优先队列式分支限界法解装载问题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的时候,可以将右⼦树剪去。

分支限界法经典案例算法分析

分支限界法经典案例算法分析

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背包问题。 例如:

分枝限界法实验报告(3篇)

分枝限界法实验报告(3篇)

第1篇一、实验目的1. 理解并掌握分枝限界法的基本原理和实现方法。

2. 通过实际编程,运用分枝限界法解决实际问题。

3. 比较分析分枝限界法与其他搜索算法(如回溯法)的优缺点。

4. 增强算法设计能力和编程实践能力。

二、实验内容本次实验主要涉及以下内容:1. 分支限界法的基本概念和原理。

2. 分支限界法在单源最短路径问题中的应用。

3. 分支限界法的实现步骤和代码编写。

4. 分支限界法与其他搜索算法的对比分析。

三、实验环境1. 操作系统:Windows 102. 编程语言:Python3.83. 开发环境:PyCharm四、实验步骤1. 算法描述:分支限界法是一种用于解决组合优化问题的算法,其基本思想是在问题的解空间树中,按照一定的搜索策略,优先选择有潜力的节点进行扩展,从而减少搜索空间,提高搜索效率。

2. 程序代码:下面是使用Python实现的分支限界法解决单源最短路径问题的代码示例:```pythonimport heapqclass Node:def __init__(self, vertex, distance, parent): self.vertex = vertexself.distance = distanceself.parent = parentdef __lt__(self, other):return self.distance < other.distancedef branch_and_bound(graph, source):初始化优先队列和已访问节点集合open_set = []closed_set = set()添加源节点到优先队列heapq.heappush(open_set, Node(source, 0, None))主循环,直到找到最短路径while open_set:弹出优先队列中最小距离的节点current_node = heapq.heappop(open_set)检查是否已访问过该节点if current_node.vertex in closed_set:continue标记节点为已访问closed_set.add(current_node.vertex)如果当前节点为目标节点,则找到最短路径if current_node.vertex == target:path = []while current_node:path.append(current_node.vertex)current_node = current_node.parentreturn path[::-1]遍历当前节点的邻居节点for neighbor, weight in graph[current_node.vertex].items():if neighbor not in closed_set:计算新节点的距离distance = current_node.distance + weight添加新节点到优先队列heapq.heappush(open_set, Node(neighbor, distance, current_node))没有找到最短路径return None图的表示graph = {0: {1: 2, 2: 3},1: {2: 1, 3: 2},2: {3: 2},3: {1: 3}}源节点和目标节点source = 0target = 3执行分支限界法path = branch_and_bound(graph, source)print("最短路径为:", path)```3. 调试与测试:在编写代码过程中,注意检查数据结构的使用和算法逻辑的正确性。

旅行售货员问题(分支限界法)

旅行售货员问题(分支限界法)

旅⾏售货员问题(分⽀限界法)⼀、实验内容运⽤分⽀限界法解决0-1背包问题(或者旅⾏售货员问题、或者装载问题、或者批处理作业调度)使⽤优先队列式分⽀限界法来求解旅⾏售货员问题⼆、所⽤算法基本思想及复杂度分析1.算法基本思想分⽀限界法常以⼴度优先或以最⼩耗费有限的⽅式搜索问题的解空间树。

问题的解空间树是表⽰问题解空间的⼀棵有序树,常见的有⼦集树和排列树。

在搜索问题的解空间树时,分⽀限界法和回溯法的主要区别在于它们对当前扩展节点所采⽤的扩展⽅式不同。

在分⽀限界法中,每⼀个活结点只有⼀次机会成为扩展节点。

活结点⼀旦成为扩展节点,就⼀次性产⽣其所有⼉⼦节点。

在这些⼉⼦节点中,导致不可⾏解或导致⾮最优解的⼉⼦节点被舍弃,其余⼉⼦节点被加⼊活结点表中。

此后,从活结点表中取下⼀节点为当前扩展节点。

并重复上述节点扩展过程。

这个过程移⾄持续到找到所需的解或活结点表为空为⽌。

从活结点表中选择下⼀扩展节点的不同⽅式导致不同的分⽀限界法。

最常见的有以下两种⽅式:(1)队列式分⽀限界法队列式分⽀限界法将活结点表组织成⼀个队列,并按队列的先进先出原则选取下⼀个节点为当前扩展节点。

(2)优先队列式分⽀限界法优先队列式的分⽀限界法将活结点表组织成⼀个优先队列,并按优先队列中规定的节点优先级选取优先级最⾼的下⼀个节点成为当前扩展节点。

2.问题分析及算法设计问题分析:(1)解旅⾏售货员问题的优先队列式分⽀限界法⽤优先队列存储活结点表。

(2)活结点m在优先队列中的优先级定义为:活结点m对应的⼦树费⽤下界lcost。

(3)lcost=cc+rcost,其中,cc为当前结点费⽤,rcost为当前顶点最⼩出边费⽤加上剩余所有顶点的最⼩出边费⽤和。

(4)优先队列中优先级最⼤的活结点成为下⼀个扩展结点。

(5)排列树中叶结点所相应的载重量与其优先级(下界值)相同,即:叶结点所相应的回路的费⽤(bestc)等于⼦树费⽤下界lcost的值。

算法设计:(1)要找最⼩费⽤旅⾏售货员回路,选⽤最⼩堆表⽰活结点优先队列。

第6章__分支限界法

第6章__分支限界法
20
算法的改进

节点的左子树表示将此集装箱装上船,右 子树表示不将此集装箱装上船。

设bestw是当前最优解;ew是当前扩展结点所相 应的重量;r是剩余集装箱的重量。则当 ew+rbestw时,可将其右子树剪去,因为此时 若要船装最多集装箱,就应该把此箱装上船。

另外,为了确保右子树成功剪枝,应该在 算法每一次进入左子树的时候更新bestw的 值。
28
优先队列式分支限界法

在优先队列式分支限界法中,一旦有一个叶结 点成为当前扩展结点,则可以断言该叶结点所 相应的解即为最优解。此时可终止算法。 最大优先级队列中的活节点都是互相独立的, 因此每个活节点内部必须记录从子集树的根到 此节点的路径。 一旦找到了最优装载所对应的叶节点,就利用 这些路径信息来计算x 值。
分支限界法
1
提纲
分支限界法的基本思想 装载问题 布线问题 0-1背包问题 小结

2
提纲
分支限界法的基本思想 装载问题 布线问题 0-1背包问题 小结

3
分支限界法的基本思想

分支限界法与回溯法的不同

求解目标

回溯法:所有解 分支限界法:找出满足约束条件的一个解,或是满 足约束条件的解中找出在某种意义下的最优解。 回溯法:深度优先搜索解空间树 分支限界法:广度优先或以最小耗费(最大收益)优先 的方式搜索解空间树。
25
构造最优解
算法EnQueue做如下修改: void EnQueue(…..) {// 如不是叶节点,向Q中添加一个i 层、重量为wt的活节点 // 新节点是E的孩子。当且仅当是左孩子时,ch为true。 if (i == n) // 叶子 { if (wt == bestw) // 目前的最优解 { bestE = E; bestx[n] = ch;} return; } QNode<T> *b; // 不是叶子, 添加到队列中 b = new QNode<T>; b->weight = wt; b->parent = E; b->LChild = ch; Q.Add(b); }

单元最短路径问题 分支限界法

单元最短路径问题 分支限界法

单元最短路径问题分支限界法标题:解密单元最短路径问题:深度探索分支限界法一、引言单元最短路径问题是图论中的一个经典问题,它在实际生活中有着广泛的应用。

而分支限界法则是解决这一问题的重要方法之一。

本文将深入探讨单元最短路径问题,并重点介绍分支限界法在解决该问题中的应用。

二、单元最短路径问题概述单元最短路径问题是指在一个加权有向图中,求出从一个指定起始顶点到其他所有顶点的最短路径。

这个问题可以用于交通规划、网络通信以及物流配送等领域。

在实际生活中,我们经常需要求解单元最短路径问题来优化路线或资源利用。

三、分支限界法介绍分支限界法是一种用于求解最优化问题的通用技术。

它通过不断地扩展候选解空间,并在搜索过程中剪枝,以获得最优解。

在解决单元最短路径问题中,分支限界法可以通过不断地搜索路径长度,并在搜索过程中淘汰一些非最优路径,从而高效地找到最短路径。

四、分支限界法在单元最短路径问题中的应用在实际应用中,我们可以将单元最短路径问题转化为一个树型图的搜索问题,在搜索过程中使用分支限界法来逐步缩小解空间。

通过递归地搜索各条路径,并不断更新最短路径的长度,我们可以最终找到起始顶点到其他所有顶点的最短路径。

分支限界法可以在搜索过程中灵活地调整搜索策略,从而有效地优化解的搜索过程。

五、个人观点和理解我个人认为,分支限界法作为一种智能化的搜索算法,在解决单元最短路径问题时具有独特的优势。

它可以根据实际问题的特点,灵活地调整搜索策略,以获得更高效的搜索结果。

分支限界法也可以在处理大规模数据时,通过剪枝等策略,节省搜索时间和空间成本。

六、总结和回顾通过本文的讨论,我们对单元最短路径问题和分支限界法有了更深入的理解。

分支限界法作为一种重要的搜索算法,在解决单元最短路径问题时发挥着重要作用。

我们希望读者可以通过本文的介绍,对这一话题有更全面、深刻和灵活的理解,以应用于实际问题中。

通过上述深度探索,我们对单元最短路径问题和分支限界法有了更清晰的认识。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
2、时间复杂度 Maxloading 算法计算时间和空间复杂性均为 O(2n)。
3、可能的改进 节点的左子树表示将此集装箱装上船,右子树表示不将此集装箱装上船。设 bestw 是当前最 优解;wt 是当前扩展结点所相应的重量;r 是剩余集装箱的重量。则当 wt+rbestw 时,可 将其右子树剪去,因为此时若要船装最多集装箱,就应该把此箱装上船。 另外,为了确保 右子树成功剪枝,应该在算法每一次进入左子树的时候更新 bestw 的值。
}
int IsEmpty() {
if(Q->next==NULL) return 1; return 0; }
int Delete(Queue* &E) {
Queue* tmp = NULL; tmp = fq; E = fq; Q->next = fq->next; /*一定不能丢了链表头*/ fq = fq->next; return 0; }
int Add(int w, Queue* E, bool l) {
Queue* q; q = new Queue; q->next = NULL; q->weight = w; q->parent = E; q->LChild = l; if(Q->next == NULL) {
Q->next = q; fq = lq = Q->next; //一定要使元素放到链中 } else { lq->next = q; lq = q; } return 0;
二、描述问题
有一批共 n
n
个集装箱要装上
2
艘载重量分别为
c1

c2 的轮船,其中集装箱
i
的重量为
wi ,且 wi c1 c2 ,要求确定是否有一个合理的装载方案可将这 n 个集装箱装上这 2
艘轮船。i如1 果有,请给出该方案。
三、由原理得到的算法、算法的复杂度、改进
1、 可得算法 算法 enquence 将活结点加入队列。若 i=n,则表示当前为叶节点,此时只要检查对应 的可行解是否为最优解。i<n 时,当期结点为内部节点,加入队列中。 在算法的 while 循环中,首先检测当前扩展结点的左儿子结点是否为可行结点。如果是则将 其加入到活结点队列中。然后将其右儿子结点加入到活结点队列中(右儿子结点一定是可行 结点)。2 个儿子结点都产生后,当前扩展结点被舍弃。 活结点队列中的队首元素被取出作为当前扩展结点,由于队列中每一层结点之后都有一个尾 部标记-1,故在取队首元素时,活结点队列一定不空。当取出的元素是-1 时,再判断当前队 列是否为空。如果队列非空,则将尾部标记-1 加入活结点队列,算法开始处理下一层的活 结点。
} 构造最优解: 为了在算法结束后能方便地构造出与最优值相应的最优解,算法必须存储相应子集树中从活 结点到根结点的路径。为此目的,可在每个结点处设置指向其父结点的指针,并设置左儿子 标志。
template<class Type> class QNode {
friend void EnQueue(queue<QNode <Type> * >&,Type,int,int,Type,QNode <Type> *,QNode <Type> * &,int*,bool);
(1)求解目标:回溯法的求解目标是找出解空间树中满足约束条件的所有解,而分 支 限 界 法 的 求 解 目 标 则 是 找 出 满 足 约 束 条 件 的 一 个 解 ,或 是 在 满 足 约 束 条 件 的 解 中 找 出在某种意义下的最优解。 (2)搜索方式的不同:回溯法以深度优先的方式搜索解空间树,而分支限界法则以 广度优先或以最小耗费优先的方式搜索解空间树。
{ if(wt>bestw) bestw = wt; EnQueue(Ew + w[i], bestw,i,n,E,bestE,1);
} // 右孩子总是可行的 EnQueue(Ew, bestw, i, n, E, bestE, 0); Delete(E); // 取下一个扩展结点 if (E!=NULL&&E->weight == -1) { // 到达层的尾部
void EnQueue(int wt,int& bestw, int i, int n, Queue*E,Queue*&bestE, bool ch) // 该函数负责加入活结点 { // 如果不是叶结点,则将结点权值 wt 加入队列 Q
if (i == n) {
if(wt == bestw) {
if (i == n) {
if(wt == bestw) {
//当前最优载重量 bestE = E; bestx[n] = ch; } return; } Add(wt, E, ch); // 不是叶子 } int MaxLoading(int w[], int c, int n) { // 返回最优装载值 // 为层次 1 初始化 int err; //返回值 int i = 1; // 当前扩展结点的层 int Ew = 0; // 当前扩展结点的权值 int r = 0 ; //剩余集装箱重量 Queue *E=0 ; //当前扩展结点 //*bestE 当前最优扩展结点 for(int j =2; j<=n; j++) r += w[j]; bestw = 0; // 目前的最优值 Q =new Queue; Q->next = NULL; Q->weight = -1; err = Add(-1, NULL, 0); //标记本层的尾部 if(err) { return 0; } while (true) { int wt = Ew + w[i]; // 检查左孩子结点 if (wt <= c)
int Ew = 0; // 当前扩展结点的权值 int r = 0 ; //剩余集装箱重量 Queue *E=0 ; //当前扩展结点 //*bestE 当前最优扩展结点 for(int j =2; j<=n; j++)
r += w[j]; bestw = 0; // 目前的最优值 Q =new Queue; Q->next = NULL; Q->weight = -1; err = Add(-1, NULL, 0); //标记本层的尾部 if(err) {
分支限界法求解装载问题
一、方法一般原理
分支限界法常以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树。 在分支限界法中,每一个活结点只有一次机会成为扩展结点。活结点一旦成为扩展结点, 就一次性产生其所有儿子结点。在这些儿子结点中,导致不可行解或导致非最优解的儿子结 点被舍弃,其余儿子结点被加入活结点表中。 此后,从活结点表中取下一结点成为当前扩展结点,并重复上述结点扩展过程。这个过程一 直持续到找到所需的解或活结点表为空时为止。 两种分支限界法 (1)队列式(FIFO)分支限界法 按照队列先进先出(FIFO)原则选取下一个节点为扩展节点。 (2)优先队列式分支限界法 按照优先队列中规定的优先级选取优先级最高的节点成为当前扩展节点。
//当前最优载重量 bestE = E; bestx[n] = ch; } return; } Add(wt, E, ch); // 不是叶子 }
int MaxLoading(int w[], int c, int n) { // 返回最优装载值 // 为层次 1 初始化
int err; //返回值 int i = 1; // 当前扩展结点的层
六、附录(源码)
#include<iostream> using namespace std;
class Queue
{public:
Queue* parent; // 指向父结点的指针
bool LChild;
// 左儿子标志
ห้องสมุดไป่ตู้
int weight;
// 结点所相应的载重量
Queue* next;
};
int *bestx; int bestw=0; // 目前的最优值 Queue* Q; // 活结点队列 Queue* bestE; //当前最优扩展结点 Queue* lq = NULL; Queue* fq = NULL;
五、总结
由此,我们可以总结出分 支 限 界 法 的 设 计 思 路 :
设求解最大化问题,解向量为 X=(x1,…,xn),xi 的取值范围为 Si,|Si|=ri。在使用 分支限界搜索问题的解空间树时,先根据限界函数估算目标函数的界[down, up],然 后从根结点出发,扩展根结点的 r1 个孩子结点,从而构成分量 x1 的 r1 种可能的取 值方式。 对这 r1 个孩子结点分别估算可能的目标函数 bound(x1),其含义:以该结点为根的子 树所有可能的取值不大于 bound(x1),即: bound(x1)≥bound(x1,x2)≥…≥ bound(x1,…,xn) 若 某 孩 子 结 点 的 目 标 函 数 值 超 出 目 标 函 数 的 下 界 ,则 将 该 孩 子 结 点 丢 弃 ;否 则 ,将 该 孩子结点保存在待处理结点表 PT 中。 再取 PT 表中目标函数极大值结点作为扩展的根结点,重复上述。 直到一个叶子结点 时的可行解 X=(x1,…,xn),及目标函数值 bound(x1,…,xn)。 分支限界法与回溯法的不同:
while(true)
{ //检查左儿子结点 Type wt =Ew + w[i];//左儿子结点的重量 if(wt <= c) { //可行结点 if(wt>bestw) bestw = wt; //加入活结点队列 if(i<n) Q.push(wt); } //检查右儿子结点 if(Ew+r>bestw&&i<n) Q.push(Ew);//可能含最优解 Ew = Q.front(); Q.pop(); //取下一扩展结点 if(Ew == -1) { //同层结点尾部 if(Q.empty()) return bestw; Q.push(-1);//同层结点尾部标志 Ew = Q.front(); Q.pop();//取下一扩展结点 i++;//进入下一层 r-=w[i]; }
相关文档
最新文档