0039算法笔记——【分支限界法】电路板排列问题

     问题描述

     将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。

0039算法笔记——【分支限界法】电路板排列问题_第1张图片       0039算法笔记——【分支限界法】电路板排列问题_第2张图片

     左上图中,跨越插槽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;
}
    程序运行结果如图:

0039算法笔记——【分支限界法】电路板排列问题_第3张图片

你可能感兴趣的:(算法笔记,最优排列密度,电路板排列问题,优先级队列分支限界法)