佈線問題 隊列式分支算法

印刷電路板將佈線區域劃分成n*m個方格陣列。精確的電路佈線問題要求確定鏈接方格a到方格b的最短佈線方案。在佈線時,電路只能沿直線或者直角佈線。爲了避免線路相交,已布了線的方格做了封鎖標記(紅色方格),其他線路不允許穿過被封鎖的方格。





下面討論用隊列式分支界限法來解決佈線問題。佈線問題的解空間是一個圖。解此問題的隊列式分支界限法從起始位置a開始將它作爲第一個擴展結點。與該結點相鄰並且可達的方格成爲可行結點被加入到活結點隊列中,並且將這些方格標記1,即從起始方格a到這些方格的距離爲1.接着從活結點隊列中取出首結點作爲下一個擴展結點,並將與當前擴展結點相鄰且未標記過的方格標記2,並存入活結點隊列。這個過程一直持續到算法搜索到目標方格b或者活結點隊列爲空爲止。





在實現上述算法時,首先定義一個表示電路板上方格位置的類Position,它的兩個私有成員row和col分別表示方格所在的行和列。在電路板的任何一個方格處,佈線可沿右,下,左,上4個方向進行。沿這4個方向的移動分別記爲移動0,1,2,3.下表的offset[i].row 和offset[i].col分別給出4個方向的相對位移。


用二維數組grid表示所給方陣。初始時grid[i][j]=0,表示該方格允許佈線,而grid[i][j]=1表示該方格被封鎖,不允許佈線。爲了方便處理方格邊界的情況,給方陣的四周設置一道圍牆,即把周圍用附加的已封鎖方格(g[i][j]=1)圍住。算法一開始先檢驗起始方格和目的方格是否同位置,如果是,則不必計算,直接返回最短距離0。由於數字0和1用於表示方格的開放/封鎖狀態,所以表示距離時不用這兩個數字,將距離的值都加2。實際距離爲標記距離減2。算法具體的描述如下:

bool FindPath(Position start,Position finish,int &PathLen,Position *&Path)
{
	//計算從起始位置start到目標位置finish的最短佈線路徑
	//找到最短佈線路徑返回true,否則返回false
	if((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 NumOfNbrs=4;  //相鄰方格數
	Position here,nbr;
	here.row=start.row;
	here.col=start.col;
	grid[start.row][start.col]=2;
	//標記可達方格位置
	LinkedQueue<Position>Q;
	do{//標記可達相鄰方格
		for(int i=0;i<NumOfNbr;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<NumOfNbr;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;
}





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章