0039算法笔记——【分支限界法】电路板排列问题
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
算法分析与设计(第2版)分支限界法

(1)求解目标:回溯法的求解目标是找出解空间树中满 足约束条件的所有解,而分支限界法的求解目标则是找出 满足约束条件的一个解,或是在满足约束条件的解中找出 在某种意义下的最优解。 (2)搜索方式的不同:回溯法以深度优先的方式搜索解 空间树,而分支限界法则以广度优先或以最小耗费优先的 方式搜索解空间树。
2. 分支限界法基本思想
分支限界法常以广度优先或以最小耗费(最大效益) 优先的方式搜索问题的解空间树。在分支限界法中,每一 个活结点只有一次机会成为扩展结点。活结点一旦成为扩 展结点,就一次性产生其所有儿子结点。在这些儿子结点 中,导致不可行解或导致非最优解的儿子结点被舍弃,其 余儿子结点被加入活结点表中。此后,从活结点表中取下 一结点成为当前扩展结点,并重复上述结点扩展过程。这 个过程一直持续到找到所需的解或活结点表为空时为止。
第6章 分支限界法
本章主要知识点
• 6.1 分支限界法的基本思想
•
• • • •
6.2 单源最短路径问题
6.3 装载问题 6.4 布线问题 6.5 6.6 0-1背包问题 最大团问题
•
• •
6.7
6.8 6.9
旅行售货员问题
电路板排列问题 批处理作业调度
6.1 分支限界法的基本思想
1. 分支限界法与回溯法的不同
2. 算法思想
解单源最短路径问题的优先队列式分支限界法用一 极小堆来存储活结点表。其优先级是结点所对应的当前 路长。 算法从图G的源顶点s和空优先队列开始。结点s被扩 展后,它的儿子结点被依次插入堆中。此后,算法从堆 中取出具有最小当前路长的结点作为当前扩展结点,并 依次检查与当前扩展结点相邻的所有顶点。如果从当前 扩展结点i到顶点j有边可达,且从源出发,途经顶点i再 到顶点j的所相应的路径的长度小于当前最优路径长度, 则将该顶点作为活结点插入到活结点优先队列中。这个 结点的扩展过程一直继续到活结点优先队列为空时为止。
学习电脑信息五大常用算法之五:分支限界法

五大常用算法之五:分支限界法五大常用算法之五:分支限界法分支限界法一、基本描述类似于回溯法,也是一种在问题的解空间树T上搜索问题解的算法。
但在一般情况下,分支限界法与回溯法的求解目标不同。
回溯法的求解目标是找出T中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出使某一目标函数值达到极大或极小的解,即在某种意义下的最优解。
(1)分支搜索算法所谓“分支”就是采用广度优先的策略,依次搜索E-结点的所有分支,也就是所有相邻结点,抛弃不满足约束条件的结点,其余结点加入活结点表。
然后从表中选择一个结点作为下一个E-结点,继续搜索。
选择下一个E-结点的方式不同,则会有几种不同的分支搜索方式。
1)FIFO搜索2)LIFO搜索3)优先队列式搜索(2)分支限界搜索算法二、分支限界法的一般过程由于求解目标不同,导致分支限界法与回溯法在解空间树T上的搜索方式也不相同。
回溯法以深度优先的方式搜索解空间树T,而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间树T。
分支限界法的搜索策略是:在扩展结点处,先生成其所有的儿子结点(分支),然后再从当前的活结点表中选择下一个扩展对点。
为了有效地选择下一扩展结点,以加速搜索的进程,在每一活结点处,计算一个函数值(限界),并根据这些已计算出的函数值,从当前活结点表中选择一个最有利的结点作为扩展结点,使搜索朝着解空间树上有最优解的分支推进,以便尽快地找出一个最优解。
分支限界法常以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树。
问题的解空间树是表示问题解空间的一棵有序树,常见的有子集树和排列树。
在搜索问题的解空间树时,分支限界法与回溯法对当前扩展结点所使用的扩展方式不同。
在分支限界法中,每一个活结点只有一次机会成为扩展结点。
活结点一旦成为扩展结点,就一次性产生其所有儿子结点。
在这些儿子结点中,那些导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被子加入活结点表中。
分支限界法

之和;bestp表示当前最优解,初始值为0。 当cp>bestp时,更新bestp为cp。
(7,0) A
(7,0) A x1=1 (4,9) B x1=0 C (7,0)
(7,0) A x1=1 (4,9) B x2=0 (4,9) D x1=0 C (7,0)
1
50 30
2 4
15
5 3
3
4
问题的解空间(x1,x2,x3,x4),其中令
S={1,2,3,4}, x1=1,x2∈S-{x1},x3∈S{x1,x2},x4 ∈ S-{x1,x2,x3}。 解空间的组织结构是一棵深度为4的排列树。 搜索
约束条件g[i][j]!=∞,其中g是该图的邻接矩阵;
F
x4=2
12
G
12+30=42
A0 x1=1 0 x2=2 30
A0 x1=1 0
x2=4 4 x3=2 19
A
3
A 3 x2=4 4 D x3=2 x3=3 7 F x4=2 12 G 12+30=42
x2=2
B
50
C E
D
x3=3 7
30
B
50
C
F
x4=2
x3=3 35
A0 x1=1 0 x2=2 30 x3=3 35 H B 50 A 3 C x2=4 4 D x3=2 19 E x3=3 7 F x4=2 12 G 12+30=42
H
19 E
12
G
12+30=42
算法优化
估计路径长度的下界 用zl表示,zl=cl+rl
优先级:活结点的zl,zl越小,优先级越高; 约束条件:同上
第6章 分支限界法

6.3 装载问题
1. 问题描述
• 有一批共个集装箱要装上 2 艘载重量分别为 C1 和C2的轮船,其中集装箱i的重量为wi,且
w
i 1
n
i
c1 c2
• 装载问题要求确定是否有一个合理的装载方案 可将这个集装箱装上这2艘轮船。如果有,找出 一种装载方案。 • 如果一个给定装载问题有解,则采用装载策略
最短路径问题关键部分算法
while (true){ // 搜索问题的解空间 for (int j=1;j<=n;j++) if(c[E.i][j] <max && E.length+cE.i][j] < dist[j]) { // 顶点i到顶点j可达,且满足控制约束 dist[j]=E.length+c[E.i][j]; //修正数值
bestx[j] = (bestE.LChild) ? 1 : 0; bestE = bestE.parent; }
学习要点
理解分支限界法的剪枝搜索策略 掌握分支限界法的算法框架,会描述队列变化
(1)队列式(FIFO)分支限界法
(2)优先队列式分支限界法 :会确定优先级
通过应用范例学习分支限界法的设计策略
掌握两类例子每类2个例子:子集树、排列树
分支限界法与回溯法
(1)求解目标:基本相同 回溯法:通过回溯找出满足约束条件的所有解或最 优解 分支限界法:找出满足约束条件的一个解,或是在 满足约束条件的解中找出在某种意义下的最优解 (2)搜索方式:不同
3. 约束条件 1)显式约束:对分量xi的取值限定。
2)隐式约束:为满足问题的解而对不同分量之 间施加的约束。 4. 解空间:对于问题的一个实例,解向量满足显式 约束条件的所有多元组,构成了该实例的一个解空间。
分支限界法电路板排列问题c++实现

分支限界法是一种常用的解决组合优化问题的算法,它通过不断扩展当前解空间的分支节点,并通过限界函数对分支节点进行剪枝,最终找到最优解。
在本文中,我们将使用C++语言实现分支限界法来解决电路板排列问题。
电路板排列问题是一个经典的组合优化问题,其目标是将给定数量的电路板排列在一个给定大小的板子上,使得每块电路板之间不发生重叠。
假设每块电路板都是矩形形状,那么问题就是确定每块电路板在板子上的位置和角度,使得它们不重叠,并尽可能地节省空间。
现在让我们通过C++语言来实现分支限界法来解决电路板排列问题。
1. 设计电路板类我们首先需要设计一个电路板类,用来表示每块电路板的属性,包括长度、宽度、位置和角度等。
以下是电路板类的定义:```cppclass CircuitBoard {public:int length; // 电路板长度int width; // 电路板宽度int x; // 电路板左上角x坐标int y; // 电路板左上角y坐标float angle; // 电路板旋转角度};```2. 实现分支限界法接下来,我们需要实现分支限界法来解决电路板排列问题。
我们将通过递归的方式来搜索最优解,具体步骤如下:- 初始化一个优先队列,用来存储待扩展的分支节点。
- 将初始节点加入优先队列中。
- 当优先队列不为空时,循环执行以下步骤:- 从优先队列中取出一个节点。
- 判断该节点是否为叶子节点,如果是则更新最优解。
- 否则,对该节点进行扩展,计算每个子节点的限界值,并将其加入优先队列中。
- 继续循环直到找到最优解或者遍历完所有节点。
3. 编写C++代码现在让我们编写C++代码来实现上述算法。
```cpp#include <iostream>#include <vector>#include <queue>using namespace std;class CircuitBoard {public:int length; // 电路板长度int width; // 电路板宽度int x; // 电路板左上角x坐标int y; // 电路板左上角y坐标float angle; // 电路板旋转角度};bool isOverlap(const CircuitBoard board1, const CircuitBoard board2) {// 判断两块电路板是否重叠// ...}int boundingFunction(const vector<CircuitBoard>currentSolution, const vector<CircuitBoard> rem本人ningBoards) {// 计算限界值// ...}void branchAndBound(const vector<CircuitBoard> initialBoards, int boardCount, int boardWidth, int boardLength) {priority_queue<vector<CircuitBoard>,vector<vector<CircuitBoard>>,pare> pq;vector<CircuitBoard> currentSolution;vector<CircuitBoard> rem本人ningBoards = initialBoards;int bestSolution = INT_MAX;pq.push(initialBoards);while (!pq.empty()) {vector<CircuitBoard> currentNode = pq.top();pq.pop();if (currentNode.empty()) {bestSolution = min(bestSolution,calculateSolution(currentSolution));} else {vector<CircuitBoard> leftNode =expandNode(currentNode, true);vector<CircuitBoard> rightNode =expandNode(currentNode, false);int boundLeft = boundingFunction(leftNode, rem本人ningBoards);int boundRight = boundingFunction(rightNode, rem本人ningBoards);if (boundLeft < bestSolution) {pq.push(leftNode);}if (boundRight < bestSolution) {pq.push(rightNode);}}}}int m本人n() {// 读入电路板信息// ...// 调用分支限界法求解// ...return 0;}```4. 总结在本文中,我们使用C++语言实现了分支限界法来解决电路板排列问题。
算法分析与设计(第2版)分支限界法
}
private static void enqueue(int wt, int i) { if(i==n) //如果到达叶子 { if(wt>bestw) //如果该叶子代表的解优于当前最优解 bestw=wt; } else queue.put(new Integer(wt)); }
}
2. 算法的改进
2. 算法思想
解单源最短路径问题的优先队列式分支限界法用一 极小堆来存储活结点表。其优先级是结点所对应的当前 路长。 算法从图G的源顶点s和空优先队列开始。结点s被扩 展后,它的儿子结点被依次插入堆中。此后,算法从堆 中取出具有最小当前路长的结点作为当前扩展结点,并 依次检查与当前扩展结点相邻的所有顶点。如果从当前 扩展结点i到顶点j有边可达,且从源出发,途经顶点i再 到顶点j的所相应的路径的长度小于当前最优路径长度, 则将该顶点作为活结点插入到活结点优先队列中。这个 结点的扩展过程一直继续到活结点优先队列为空时为止。
结点的左孩子表示将当前集装箱装上船,右孩子表示不将当前 集装箱装上船。设bestw是当前所求到的所有可行解中装上船的集装 箱重量和的最优解;ew是当前扩展结点所表示的已经装上船的集装 箱重量和;r是目前尚未考虑的剩余集装箱的重量和。则当 ew+rbestw时,可将其右子树剪去,因为,此时即使把剩余的集装 箱全部装上船,也不会的到比bestw更优的解。 另外,为了确保右子树成功剪枝,应该在算法每一次进入左子 树的时候更新bestw的值。
在解空间树中,如果以结点y为根的子树中所含的解 优于以结点x为根的子树中所含的解,则称结点y控制了结 点x,以被控制的结点x为根的子树可以剪去。
public class BBShortest { static class HeapNode implements Comparable { int i; float length; HeapNode(int ii, float ll) { i=ii; l=ll; } public int compareto(Object x) { float xl=((HeapNode)x).length; if(length<xl) return -1; if(length==xl) return 0; return 1; } } static float [][]a; public static void shortest(int v, float []dist, int []p) { int n=p.length-1; MinHeap heap=new MinHeap(); HeapNode enode=new HeapNode(v,0);//定义源为初始扩展结点 for(int j=1; j<=n; j++) dist[j]=Float.MAX-VALUE;//从源到各顶点的初始距离是无穷 dist[v]=0;
电路板排列密度分支限界
电路板排列密度分支限界电路板排列密度分支限界是一个用于在电路板设计和布局过程中确定排列密度的技术指标。
它是根据电路板上元器件的和连接线的分布情况来进行界定的,以保证电路板的性能和稳定性。
排列密度是指在给定面积上所能容纳的元器件和连接线的数量。
在电路板设计的过程中,我们需要合理安排元器件的位置和连接线的布局,以使得电路板可以正常工作,并且避免元器件之间的干扰和相互影响。
由于电路板的面积有限,排列密度是一个重要的考虑因素。
如果元器件排列太密集,可能会导致元器件之间的短路或干扰,影响电路的性能。
如果排列过于稀疏,可能会浪费电路板上的空间,使得整个电路板过大,增加成本和功耗。
在电路板设计中需要根据实际情况确定排列密度的分支限界。
根据电路板设计的要求和元器件的特性,可以考虑以下因素来确定排列密度的分支限界:1. 元器件尺寸和间距:根据每个元器件的尺寸和间距要求,确定元器件的最小安装间距和最小连线间距,以确保元器件之间具有足够的空隙和隔离距离。
2. 热量分散:考虑元器件的热量分散和散热要求,避免元器件过度集中而导致热量难以散发,影响整个电路板的稳定性。
3. 信号和电源干扰:根据元器件的信号和电源要求,避免不同信号线或电源线之间的干扰和串扰,以确保电路的可靠性和稳定性。
4. 工艺限制:考虑到制造工艺的限制,避免元器件的尺寸过小或排列过于复杂,增加制造难度和成本。
最终确定的排列密度分支限界应该是一个平衡点,既要满足电路的性能和稳定性要求,又要考虑成本和制造难度等因素。
根据不同的电路需求和设计要求,排列密度的分支限界也会有所不同。
在电路板设计中,工程师需要综合考虑以上因素,并进行合理的评估和决策,以确保电路板的性能和可靠性。
布线问题(分支限界法)
布线问题(分⽀限界法)⼀、⾸先说⼀下分⽀限界法的思想:(1)⽐较:分⽀限界法和回朔法有相似之处,但是回朔法是搜索问题的所有解,采⽤深度优先搜索;⽽分⽀限界法是搜索问题的最优解,采⽤的是⼴度优先搜索;(2)核⼼思想:分⽀限界法中,每⼀个活节点都只有⼀次机会成为扩展节点。
活节点⼀旦成为扩展节点,就⼀次性产⽣所有的⼉⼦节点。
在这些⼉⼦节点中,导致不可⾏解或者导致⾮最优解的⼉⼦节点被舍弃,其余⼉⼦节点被加⼊活节点表中。
此后,从活节点表中取下⼀节点成为当前扩展节点,并重复上述节点的扩展过程。
这个过程⼀直在持续到找到所需要的最优解或者活节点表为空时为⽌;其中:选择扩展节点的⽅式可以分为:队列式分⽀限界法和优先队列式分⽀限界法。
后者相对于前者的改进是对活节点加⼊了优先级,优先级最⾼的成为扩展节点(通常通过最⼤堆最⼩堆实现);⼆、布线问题描述:代码如下://队列类 : LinkedQueue.h#ifndef LINKEDQUEUE_H#define LINKEDQUEUE_Htemplate <class Type>class LinkedQueue{public:LinkedQueue(){};explicit LinkedQueue(int Capacity); //创建队列bool IsEmpty(); //判断是否空bool IsFull(); //判断是否满bool Add(Type &cell); //向队列中加⼊元素bool Delete(Type &cell); //删除队列中的元素~LinkedQueue();private:Type cell;Type *ptr; //队列中的元素指针int QueueLen; //队列元素个数int QueueCapacity; //队列容量int Head;int Tail;};template <class Type>LinkedQueue<Type>::~LinkedQueue(){delete[]ptr;ptr = nullptr;}template <class Type>LinkedQueue<Type>::LinkedQueue(int Capacity){QueueCapacity = Capacity;Head = 0;Tail = 0;QueueLen = 0;ptr = new Type[QueueCapacity];}template <class Type>bool LinkedQueue<Type>::IsEmpty(){if (QueueLen == 0)return true;elsereturn false;}template <class Type>bool LinkedQueue<Type>::IsFull(){if (QueueLen == QueueCapacity)return true;elsereturn false;}template <class Type>bool LinkedQueue<Type>::Add(Type &cell){if (IsFull())return false;else{ptr[Tail] = cell;Tail++;QueueLen++;return true;}}template <class Type>bool LinkedQueue<Type>::Delete(Type &cell){if (IsEmpty())return false;else{cell = ptr[Head];Head++;QueueLen--;return true;}}#endif//使⽤分⽀限界法解决布线问题main.cpp//====================================================#include <iostream>#include "LinkedQueue.h"//#include <queue>using namespace std;int n, m; //布线盘是n * m⼤⼩class Position{public:int row;int col;};bool FindPath(int ** grid , Position start, Position finish, int &PathLen, Position * &path) {//计算从起始位置start到⽬标位置finish的最短布线路径//找到最短布线路径则返回true,否则返回flaseif ((start.row == finish.row) && (start.col == finish.col)){PathLen = 0;return true;}//设置⽅格阵列“围墙”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; //左翼和右翼}//初始化相对位移Position offset[4];offset[0].row = 0; offset[0].col = 1; //右offset[1].row = 1; offset[1].col = 0; //下offset[2].row = 0; offset[2].col = -1; //左offset[3].row = -1; offset[3].col = 0; //上int neigh_num = 4; //相邻⽅格数Position here, nbr;here.row = start.row;here.col = start.col;grid[start.row][start.col] = 2;//标记所有可以到达的⽅格位置LinkedQueue<Position> Q(n * m + 1); //队列//queue<Position> Q(); //队列//标记可到达的相邻⽅格do {for (int i = 0; i < neigh_num; i++){nbr.row = here.row + offset[i].row;nbr.col = here.col + offset[i].col;if (grid[nbr.row][nbr.col] == 0) //该⽅格未被锁定{grid[nbr.row][nbr.col] = grid[here.row][here.col] + 1;if ((nbr.row == finish.row) && (nbr.col == finish.col)) //完成布线break;Q.Add(nbr); //压⼊队列称为活节点}}//是否到达⽬标位置finish?if ((nbr.row == finish.row) && (nbr.col == finish.col)) //完成布线,是否要加这⼀步? break;//活节点队列是否⾮空if (Q.IsEmpty()) //⽆解return false;Q.Delete(here); //取下⼀个扩展节点} while (true);//构造最短布线路径PathLen = grid[finish.row][finish.col] - 2;path = new Position[PathLen];//从⽬标位置finish开始向起始位置回溯here = finish;for (int j = PathLen - 1; j >= 0; j--){path[j] = here;//找前驱位置for (int i = 0; i < neigh_num; i++){nbr.row = here.row + offset[i].row;nbr.col = here.col + offset[i].col;if (grid[nbr.row][nbr.col] == j + 2)break;}here = nbr; //向前移动}return true;}int main(void){Position start, finish; //开始位置和⽬标位置int PathLen; //最短路径的长度Position *path; //记录的最短路径cout << "请输⼊布线盘的⼤⼩,n * m 规格: " << endl;cin >> n >> m;cout << "请输⼊开始位置(x , y) :" << endl;cin >> start.col >> start.row;cout << "请输⼊结束位置(x , y) :" << endl;cin >> finish.col >> finish.row;int ** grid = new int*[n + 2];for (int i = 0; i < n + 2; i++){grid[i] = new int[m + 2];}for (int i = 0; i < n + 2; i++){for (int j = 0; j < m + 2; j++){grid[i][j] = 0;}}FindPath(grid, start, finish, PathLen, path);for (int i = 1; i <= n; i++){for (int j = 1; j <= m; j++){cout << grid[i][j] << " ";}cout << endl;}cout << "最短路径是: " << endl;cout << PathLen << endl;system("pause");return 0;}效果图类似:。
分支限界法求布线问题
布线问题:如图1所示,印刷电路板将布线区域划分成n*m个方格。
精确的电路布线问题要求确定连接方格a的中点到b的中点的最短布线方案。
在布线时,电路只能沿直线或直角布线,如图1所示。
为了避免线路相交,已经布线的方格做了封锁标记(如图1中阴影部分),其他线路不允许穿过被封锁的方格。
3 问题的算法选择题目的要求是找到最短的布线方案,从图1的情况看,可以用贪婪算法解决问题,也就是从a开始朝着b的方向垂直布线即可。
实际上,再看一下图2,就知道贪婪算法策略是行不通的。
因为已布线的放个没有规律的所以直观上说只能用搜索方法去找问题的解。
根据布线方法的要求,除边界或已布线处,每个E-结点分支扩充的方向有4个:上、下、左、右,也就是说,一个E-结点扩充后最多产生4个活结点。
以图2的情况为例,图的搜索过程如图3所示。
搜索以a为第一个E-结点,以后不断扩充新的活结点,直到b结束(当然反之也可以)。
反过来从b到a,按序号8-7-6-5-4-3-2-1就可以找到最短的布线方案。
从图3中也可以发现最短的布线方案是不唯一的。
且由此可以看出,此问题适合用分支限界搜索。
#include <stdio.h>#include <stdlib.h>typedef struct Position{int row;int col;}Position;typedef struct team{int x;int y;struct team *next;}team,*TEAM;Position start,end,path[100];TEAM team_l=NULL;int a[100][100];int m,n,path_len;void output(){int i,j;printf("\n|-------------------布线区域图-------------------|\n");for(i=0;i<m+2;i++){for(j=0;j<n+2;j++){printf("%5d",a[i][j]);}printf("\n");}printf("|------------------------------------------------|\n");return;}void input_data(){char yes;int x,y;printf("创建布线区域...\n\n");printf("请输入区域大小(行列的个数): ");scanf("%d,%d",&m,&n);printf("请输入开始点坐标(x,y): ");scanf("%d,%d",&start.row,&start.col);printf("请输入结束点坐标(x,y): ");scanf("%d,%d",&end.row,&end.col);printf("区域内是否有被占用点? (y/n) ");fflush(stdin);scanf("%c",&yes);fflush(stdin);while(yes=='y'){printf("请输入占用点的坐标(x,y): ");scanf("%d,%d",&x,&y);fflush(stdin);if(x<0 || x>m+1 || y<0 || y>n+1 || (x==start.row && y==start.col) || (x==end.row && y==end.col)){printf("输入错误,请重新输入\n");continue;}else{a[x][y]=-1;}printf("是否还有被占用点? (y/n) ");scanf("%c",&yes);fflush(stdin);}for(x=0;x<m+2;x++){a[0][x]=-1;a[m+1][x]=-1;}for(x=0;x<n+2;x++){a[x][0]=-1;a[x][n+1]=-1;}return;}void inq(Position p){TEAM t,q;q=team_l;t=(TEAM)malloc(sizeof(TEAM));t->x=p.row;t->y=p.col;t->next=NULL;if(team_l==NULL){team_l=t;return ;}while(q->next!=NULL){q=q->next;}q->next=t;return;}Position outq(){Position out;out.row=team_l->x;out.col=team_l->y;team_l=team_l->next;return out;}void find_path(){Position offset[4];Position here={start.row,start.col};Position nbr={0,0};int num_of_nbrs=4;int i,j;offset[0].row=0;offset[0].col=1; //右offset[1].row=1;offset[1].col=0; //下offset[2].row=0;offset[2].col=-1;//左offset[3].row=-1;offset[3].col=0;//上printf("\n开始搜索路径...\n");if((start.row == end.row)&&(start.col == end.col)){ path_len = 0;return;}while(1){for(i=0;i<num_of_nbrs;i++){nbr.row=here.row+offset[i].row;nbr.col=here.col+offset[i].col;if(a[nbr.row][nbr.col]==0){a[nbr.row][nbr.col]=a[here.row][here.col] + 1;if((nbr.row == end.row) && (nbr.col == end.col)) break;inq(nbr); //nbr入队}}//是否到达目标位置finishif((nbr.row == end.row) && (nbr.col == end.col)) break;//或节点队列是否为空if(team_l==NULL){printf("\n没有结果\n");return ;}here=outq();}path_len=a[end.row][end.col];here=end;for(j=path_len-1;j>=0;j--){path[j] = here;for(i = 0;i < num_of_nbrs;i++){nbr.row = here.row + offset[i].row;nbr.col = here.col + offset[i].col;if(a[nbr.row][nbr.col] == j) //+ 2)break;}here=nbr;}return;}void out_path(){int i;printf("\n路径为:\n");printf("(%d,%d) ",start.row,start.col);for(i=0;i<path_len;i++){printf("(%d,%d) ",path[i].row,path[i].col);}printf("\n");return;}void main(){input_data();output();find_path();out_path();output(); }。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
问题描述将n块电路板以最佳排列方式插入带有n个插槽的机箱中。
n块电路板的不同排列方式对应于不同的电路板插入方案。
设B={1, 2, …, n}是n块电路板的集合,L={N1, N2, …, Nm}是连接这n块电路板中若干电路板的m个连接块。
Ni是B的一个子集,且Ni中的电路板用同一条导线连接在一起。
设x表示n块电路板的一个排列,即在机箱的第i个插槽中插入的电路板编号是x[i]。
x所确定的电路板排列Density (x)密度定义为跨越相邻电路板插槽的最大连线数。
例:如图,设n=8, m=5,给定n块电路板及其m个连接块:B={1, 2, 3, 4, 5, 6, 7, 8},N1={4, 5, 6},N2={2, 3},N3={1, 3},N4={3, 6},N5={7, 8};其中两个可能的排列如图所示,则该电路板排列的密度分别是2,3。
左上图中,跨越插槽2和3,4和5,以及插槽5和6的连线数均为2。
插槽6和7之间无跨越连线。
其余插槽之间只有1条跨越连线。
在设计机箱时,插槽一侧的布线间隙由电路板的排列的密度确定。
因此,电路板排列问题要求对于给定的电路板连接条件(连接块),确定电路板的最佳排列,使其具有最小密度。
算法思路电路板排列问题的解空间是一颗排列树。
采用优先队列式分支限界法找出所给电路板的最小密度布局。
算法中采用最小堆表示活节点优先级队列。
最小堆中元素类型是BoradNode,每一个BoardNode类型的节点包含域x,表示节点所相应的电路板排列;s表示该节点已确定的电路板排列x[1:s];cd表示当前密度,now[j]表示x[1:s]中所含连接块j中的电路板数。
算法开始时,将排列树的根结点置为当前扩展结点。
在do-while循环体内算法依次从活结点优先队列中取出具有最小cd值的结点作为当前扩展结点,并加以扩展。
算法将当前扩展节点分两种情形处理:1)首先考虑s=n-1的情形,当前扩展结点是排列树中的一个叶结点的父结点。
x表示相应于该叶结点的电路板排列。
计算出与x相应的密度并在必要时更新当前最优值和相应的当前最优解。
2)当s<n-1时,算法依次产生当前扩展结点的所有儿子结点。
对于当前扩展结点的每一个儿子结点node,计算出其相应的密度node.cd。
当node.cd<bestd时,将该儿子结点N插入到活结点优先队列中。
算法具体实现如下:1、MinHeap2.h[cpp]view plain copy1.#include <iostream>2.3.template<class Type>4.class Graph;5.6.template<class T>7.class MinHeap8.{9.template<class Type>10.friend class Graph;11.public:12. MinHeap(int maxheapsize = 10);13. ~MinHeap(){delete []heap;}14.15.int Size() const{return currentsize;}16. T Max(){if(currentsize) return heap[1];}18. MinHeap<T>& Insert(const T& x);19. MinHeap<T>& DeleteMin(T &x);20.21.void Initialize(T x[], int size, int ArraySize);22.void Deactivate();23.void output(T a[],int n);24.private:25.int currentsize, maxsize;26. T *heap;27.};28.29.template <class T>30.void MinHeap<T>::output(T a[],int n)31.{32.for(int i = 1; i <= n; i++)33. cout << a[i] << " ";34. cout << endl;35.}36.37.template <class T>38.MinHeap<T>::MinHeap(int maxheapsize)39.{40. maxsize = maxheapsize;41. heap = new T[maxsize + 1];42. currentsize = 0;43.}44.45.template<class T>46.MinHeap<T>& MinHeap<T>::Insert(const T& x)47.{48.if(currentsize == maxsize)49. {50.return *this;51. }52.int i = ++currentsize;53.while(i != 1 && x < heap[i/2])54. {55. heap[i] = heap[i/2];56. i /= 2;57. }58.59. heap[i] = x;60.return *this;62.63.template<class T>64.MinHeap<T>& MinHeap<T>::DeleteMin(T& x)65.{66.if(currentsize == 0)67. {68. cout<<"Empty heap!"<<endl;69.return *this;70. }71.72. x = heap[1];73.74. T y = heap[currentsize--];75.int i = 1, ci = 2;76.while(ci <= currentsize)77. {78.if(ci < currentsize && heap[ci] > heap[ci + 1])79. {80. ci++;81. }82.83.if(y <= heap[ci])84. {85.break;86. }87. heap[i] = heap[ci];88. i = ci;89. ci *= 2;90. }91.92. heap[i] = y;93.return *this;94.}95.96.template<class T>97.void MinHeap<T>::Initialize(T x[], int size, int ArraySize)98.{99.delete []heap;100. heap = x;101. currentsize = size;102. maxsize = ArraySize;103.104.for(int i = currentsize / 2; i >= 1; i--)105. {106. T y = heap[i];107.int c = 2 * i;108.while(c <= currentsize)109. {110.if(c < currentsize && heap[c] > heap[c + 1]) 111. c++;112.if(y <= heap[c])113.break;114. heap[c / 2] = heap[c];115. c *= 2;116. }117. heap[c / 2] = y;118. }119.}120.121.template<class T>122.void MinHeap<T>::Deactivate()123.{124. heap = 0;125.}2、6d8.cpp[cpp]view plain copy1.//电路板排列问题优先队列分支限界法求解2.#include "stdafx.h"3.#include "MinHeap2.h"4.#include <iostream>5.#include <fstream>ing namespace std;7.8.ifstream fin("6d8.txt");9.10.class BoardNode11.{12.friend int BBArrangement(int **,int,int,int *&);13.public:14. operator int() const15. {16.return cd;17. }18.private:19.int *x, //x[1:n]记录电路板排列20. s, //x[1:s]是当前节点所相应的部分排列21. cd, //x[1:s]的密度22. *now; //now[j]是x[1:s]所含连接块j中电路板数23.};24.25.int BBArrangement(int **B,int n,int m,int *&bestx);26.27.int main()28.{29.int m = 5,n = 8;30.int *bestx;31.32.//B={1,2,3,4,5,6,7,8}33.//N1={4,5,6},N2={2,3},N3={1,3},N4={3,6},N5={7,8}34.35. cout<<"m="<<m<<",n="<<n<<endl;36. cout<<"N1={4,5,6},N2={2,3},N3={1,3},N4={3,6},N5={7,8}"<<endl;37. cout<<"二维数组B如下:"<<endl;38.39.//构造B40.int **B = new int*[n+1];41.for(int i=1; i<=n; i++)42. {43. B[i] = new int[m+1];44. }45.46.for(int i=1; i<=n; i++)47. {48.for(int j=1; j<=m ;j++)49. {50. fin>>B[i][j];51. cout<<B[i][j]<<" ";52. }53. cout<<endl;54. }55.56. cout<<"当前最优密度为:"<<BBArrangement(B,n,m,bestx)<<endl;57. cout<<"最优排列为:"<<endl;58.for(int i=1; i<=n; i++)59. {60. cout<<bestx[i]<<" ";61. }62. cout<<endl;63.64.for(int i=1; i<=n; i++)65. {66.delete[] B[i];67. }68.delete[] B;69.70.return 0;71.}72.73.//解电路板排列问题的优先队列式分支限界法74.int BBArrangement(int **B,int n,int m,int *&bestx)75.{76. MinHeap<BoardNode> H(1000);//活节点最小堆77. BoardNode E;78. E.x = new int[n+1];79. E.s = 0;80. E.cd = 0;81.82. E.now = new int[m+1];83.int *total = new int[m+1];84.//now[i] = x[1:s]所含连接块i中电路板数85.//total[i] = 连接块i中的电路板数86.87.for(int i=1; i<=m; i++)88. {89. total[i] = 0;90. E.now[i] = 0;91. }92.93.for(int i=1; i<=n; i++)94. {95. E.x[i] = i;//初始排列为1,2,3……n96.for(int j=1;j<=m;j++)97. {98. total[j] += B[i][j];//连接块中电路板数99. }100. }101.102.int bestd = m + 1;103. bestx = 0;104.105.do//节点扩展106. {107.if(E.s == n-1)//仅一个儿子节点108. {109.int ld = 0;//最后一块电路板的密度110.for(int j=1; j<=m; j++)111. {112. ld += B[E.x[n]][j];113. }114.if(ld<bestd)//密度更小的电路排列115. {116.delete[] bestx;117. bestx = E.x;118. bestd = max(ld,E.cd);119. }120.else121. {122.delete []E.x;123. }124.delete []E.now;125. }126.else//产生当前扩展节点的所有儿子节点127. {128.for(int i=E.s+1;i<=n;i++)129. {130. BoardNode N;131. N.now = new int[m+1];132.for(int j=1; j<=m; j++)133. {134.//新插入的电路板135. N.now[j] = E.now[j] + B[E.x[i]][j]; 136. }137.int ld = 0;//新插入的电路板密度138.for(int j=1; j<=m; j++)139. {140.if(N.now[j]>0 && total[j]!=N.now[j]) 141. {142. ld++;143. }144. }145. N.cd = max(ld,E.cd);146.if(N.cd<bestd)//可能产生更好的叶子节点147. {148. N.x = new int[n+1];149. N.s = E.s + 1;150.for(int j=1;j<=n;j++)151. {152. N.x[j] = E.x[j];153. }154. N.x[N.s] = E.x[i]; 155. N.x[i] = E.x[N.s]; 156. H.Insert(N);157. }158.else159. {160.delete []N.now; 161. }162. }163.delete []E.x;164. }//完成当前节点扩展165.if(H.Size() == 0)166. {167.return bestd;//无扩展节点168. }169. H.DeleteMin(E);170. }while(E.cd<bestd);171.172.//释放做小堆中所有节点173.do174. {175.delete []E.x;176.delete []E.now;177.if(H.Size() == 0)178. {179.break;180. }181. H.DeleteMin(E);182. }while(true);183.return bestd;184.}程序运行结果如图:。