问题描述
将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
#include <iostream> template<class Type> class Graph; template<class T> class MinHeap { template<class Type> friend class Graph; public: MinHeap(int maxheapsize = 10); ~MinHeap(){delete []heap;} int Size() const{return currentsize;} T Max(){if(currentsize) return heap[1];} MinHeap<T>& Insert(const T& x); MinHeap<T>& DeleteMin(T &x); void Initialize(T x[], int size, int ArraySize); void Deactivate(); void output(T a[],int n); private: int currentsize, maxsize; T *heap; }; template <class T> void MinHeap<T>::output(T a[],int n) { for(int i = 1; i <= n; i++) cout << a[i] << " "; cout << endl; } template <class T> MinHeap<T>::MinHeap(int maxheapsize) { maxsize = maxheapsize; heap = new T[maxsize + 1]; currentsize = 0; } template<class T> MinHeap<T>& MinHeap<T>::Insert(const T& x) { if(currentsize == maxsize) { return *this; } int i = ++currentsize; while(i != 1 && x < heap[i/2]) { heap[i] = heap[i/2]; i /= 2; } heap[i] = x; return *this; } template<class T> MinHeap<T>& MinHeap<T>::DeleteMin(T& x) { if(currentsize == 0) { cout<<"Empty heap!"<<endl; return *this; } x = heap[1]; T y = heap[currentsize--]; int i = 1, ci = 2; while(ci <= currentsize) { if(ci < currentsize && heap[ci] > heap[ci + 1]) { ci++; } if(y <= heap[ci]) { break; } heap[i] = heap[ci]; i = ci; ci *= 2; } heap[i] = y; return *this; } template<class T> void MinHeap<T>::Initialize(T x[], int size, int ArraySize) { delete []heap; heap = x; currentsize = size; maxsize = ArraySize; for(int i = currentsize / 2; i >= 1; i--) { T y = heap[i]; int c = 2 * i; while(c <= currentsize) { if(c < currentsize && heap[c] > heap[c + 1]) c++; if(y <= heap[c]) break; heap[c / 2] = heap[c]; c *= 2; } heap[c / 2] = y; } } template<class T> void MinHeap<T>::Deactivate() { heap = 0; }2、6d8.cpp
//电路板排列问题 优先队列分支限界法求解 #include "stdafx.h" #include "MinHeap2.h" #include <iostream> #include <fstream> using namespace std; ifstream fin("6d8.txt"); class BoardNode { friend int BBArrangement(int **,int,int,int *&); public: operator int() const { return cd; } private: int *x, //x[1:n]记录电路板排列 s, //x[1:s]是当前节点所相应的部分排列 cd, //x[1:s]的密度 *now; //now[j]是x[1:s]所含连接块j中电路板数 }; int BBArrangement(int **B,int n,int m,int *&bestx); int main() { int m = 5,n = 8; int *bestx; //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} cout<<"m="<<m<<",n="<<n<<endl; cout<<"N1={4,5,6},N2={2,3},N3={1,3},N4={3,6},N5={7,8}"<<endl; cout<<"二维数组B如下:"<<endl; //构造B int **B = new int*[n+1]; for(int i=1; i<=n; i++) { B[i] = new int[m+1]; } for(int i=1; i<=n; i++) { for(int j=1; j<=m ;j++) { fin>>B[i][j]; cout<<B[i][j]<<" "; } cout<<endl; } cout<<"当前最优密度为:"<<BBArrangement(B,n,m,bestx)<<endl; cout<<"最优排列为:"<<endl; for(int i=1; i<=n; i++) { cout<<bestx[i]<<" "; } cout<<endl; for(int i=1; i<=n; i++) { delete[] B[i]; } delete[] B; return 0; } //解电路板排列问题的优先队列式分支限界法 int BBArrangement(int **B,int n,int m,int *&bestx) { MinHeap<BoardNode> H(1000);//活节点最小堆 BoardNode E; E.x = new int[n+1]; E.s = 0; E.cd = 0; E.now = new int[m+1]; int *total = new int[m+1]; //now[i] = x[1:s]所含连接块i中电路板数 //total[i] = 连接块i中的电路板数 for(int i=1; i<=m; i++) { total[i] = 0; E.now[i] = 0; } for(int i=1; i<=n; i++) { E.x[i] = i;//初始排列为1,2,3……n for(int j=1;j<=m;j++) { total[j] += B[i][j];//连接块中电路板数 } } int bestd = m + 1; bestx = 0; do//节点扩展 { if(E.s == n-1)//仅一个儿子节点 { int ld = 0;//最后一块电路板的密度 for(int j=1; j<=m; j++) { ld += B[E.x[n]][j]; } if(ld<bestd)//密度更小的电路排列 { delete[] bestx; bestx = E.x; bestd = max(ld,E.cd); } else { delete []E.x; } delete []E.now; } else//产生当前扩展节点的所有儿子节点 { for(int i=E.s+1;i<=n;i++) { BoardNode N; N.now = new int[m+1]; for(int j=1; j<=m; j++) { //新插入的电路板 N.now[j] = E.now[j] + B[E.x[i]][j]; } int ld = 0;//新插入的电路板密度 for(int j=1; j<=m; j++) { if(N.now[j]>0 && total[j]!=N.now[j]) { ld++; } } N.cd = max(ld,E.cd); if(N.cd<bestd)//可能产生更好的叶子节点 { N.x = new int[n+1]; N.s = E.s + 1; for(int j=1;j<=n;j++) { N.x[j] = E.x[j]; } N.x[N.s] = E.x[i]; N.x[i] = E.x[N.s]; H.Insert(N); } else { delete []N.now; } } delete []E.x; }//完成当前节点扩展 if(H.Size() == 0) { return bestd;//无扩展节点 } H.DeleteMin(E); }while(E.cd<bestd); //释放做小堆中所有节点 do { delete []E.x; delete []E.now; if(H.Size() == 0) { break; } H.DeleteMin(E); }while(true); return bestd; }程序运行结果如图: