算法詳解之分支限界法的具體實(shí)現(xiàn)
首先我們來關(guān)注一個(gè)問題:
問題描述:
布線問題:印刷電路板將布線區(qū)域劃分成n×m個(gè)方格陣列,要求確定連接方格陣列中的方格a的中點(diǎn)到方格b的中點(diǎn)的最短布線方案。在布線時(shí),電路只能沿直線或直角布線,為了避免線路相交,已布了線的方格做了封鎖標(biāo)記,其他線路不允許穿過被封鎖的方格。如下圖所示:

算法思路:
布線問題的解空間是一個(gè)圖,則從起始位置a開始將它作為第一個(gè)擴(kuò)展結(jié)點(diǎn)。與該擴(kuò)展結(jié)點(diǎn)相鄰并可達(dá)的方格成為可行結(jié)點(diǎn)被加入到活結(jié)點(diǎn)隊(duì)列中,并且將這些方格標(biāo)記為1,即從起始方格a到這些方格的距離為1。接著,從活結(jié)點(diǎn)隊(duì)列中取出隊(duì)首結(jié)點(diǎn)作為下一個(gè)擴(kuò)展結(jié)點(diǎn),并將與當(dāng)前擴(kuò)展結(jié)點(diǎn)相鄰且未標(biāo)記過的方格標(biāo)記為2,并存入活結(jié)點(diǎn)隊(duì)列。這個(gè)過程一直繼續(xù)到算法搜索到目標(biāo)方格b或活結(jié)點(diǎn)隊(duì)列為空時(shí)為止。
在實(shí)現(xiàn)上述算法時(shí),
(1) 定義一個(gè)表示電路板上方格位置的類Position。
它的2個(gè)成員row和col分別表示方格所在的行和列。在方格處,布線可沿右、下、左、上4個(gè)方向進(jìn)行。沿這4個(gè)方向的移動(dòng)分別記為0,1,2,3。下表中,offset[i].row和offset[i].col(i= 0,1,2,3)分別給出沿這4個(gè)方向前進(jìn)1步相對(duì)于當(dāng)前方格的相對(duì)位移。
(2) 用二維數(shù)組grid表示所給的方格陣列。
初始時(shí),grid[i][j] = 0, 表示該方格允許布線,而grid[i][j] = 1表示該方格被封鎖,不允許布線。
算法圖解:
代碼貼出來:
#include <stdio.h>
typedef struct {
int row;
int col;
}Position;
int FindPath (Position start, Position finish, int &PathLen, Position *&path)
{ //計(jì)算從起始位置start到目標(biāo)位置finish的最短布線路徑,找到返回1,否則,返回0
int i;
if ((start.row = = finish.row) && (start.col = = finish.col)) {
PathLen = 0; return 0; } //start = finish
//設(shè)置方格陣列”圍墻”
for (i = 0; i <= m+1; i++)
grid[0][i] = grid[n+1][i] = 1; //頂部和底部
for (i = 0; i <= n+1; i++)
grid[i][0] = grid[i][m+1] = 1; //左翼和右翼
//初始化相對(duì)位移
int NumOfNbrs = 4; //相鄰方格數(shù)
Position offset[4], here, nbr;
offset[0].row = 0; offset[0].col = 1; //右
offset[0].row = 1; offset[0].col = 0; //下
offset[0].row = 0; offset[0].col = -1; //左
offset[0].row = -1; offset[0].row = 0; //上
here.row = start.row;
here.col = start.col;
LinkedQueue <Position> Q; //標(biāo)記可達(dá)方格位置
do {
for (i = 0; i< NumOfNbrs; i++) { //標(biāo)記可達(dá)相鄰方格
nbr.row = here.row + offset[i].row ;
nbr.col = here.col + offset[i].col;
if (grid[nbr.row][nbr.col] = = 0) { //該方格未標(biāo)記
grid[nbr.row][nbr.col] = grid[here.row][here.col] + 1;
if ((nbr.row = = finish.row) && (nbr.col = = finish.col)) break;//完成布線
Q.Add(nbr);
}
}
if ((nbr.row = = finishi.row) && (nbr.col = = finish.col)) break;//完成布線
if (Q.IsEmpty()) //活隊(duì)列是否為空
return 0; //無解
Q.delete(here); //取下一個(gè)擴(kuò)展結(jié)點(diǎn)
}while (1);
//構(gòu)造最短布線路徑
PathLen = grid[finish.row][finish.col] - 2;
path = new Position[PathLen];
here = finish;
for (int j = PathLen – 1; j >= 0; j--) { //找前驅(qū)位置
path[j] = here;
for (i = 0; i< NumOfNbrs; 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; //向前移動(dòng)
}
return 1;
}
void main ()
{
int grid[8][8];
int PathLen, *path;
Position start, finish;
start.row = 3; start.col = 2;
finish.row = 4; finish.col = 6;
FindPath (start, finish, PathLen, path);
}
代碼貼出來:
#include <stdio.h>
typedef struct {
int row;
int col;
}Position;
int FindPath (Position start, Position finish, int &PathLen, Position *&path)
{ //計(jì)算從起始位置start到目標(biāo)位置finish的最短布線路徑,找到返回1,否則,返回0
int i;
if ((start.row = = finish.row) && (start.col = = finish.col)) {
PathLen = 0; return 0; } //start = finish
//設(shè)置方格陣列”圍墻”
for (i = 0; i <= m+1; i++)
grid[0][i] = grid[n+1][i] = 1; //頂部和底部
for (i = 0; i <= n+1; i++)
grid[i][0] = grid[i][m+1] = 1; //左翼和右翼
//初始化相對(duì)位移
int NumOfNbrs = 4; //相鄰方格數(shù)
Position offset[4], here, nbr;
offset[0].row = 0; offset[0].col = 1; //右
offset[0].row = 1; offset[0].col = 0; //下
offset[0].row = 0; offset[0].col = -1; //左
offset[0].row = -1; offset[0].row = 0; //上
here.row = start.row;
here.col = start.col;
LinkedQueue <Position> Q; //標(biāo)記可達(dá)方格位置
do {
for (i = 0; i< NumOfNbrs; i++) { //標(biāo)記可達(dá)相鄰方格
nbr.row = here.row + offset[i].row ;
nbr.col = here.col + offset[i].col;
if (grid[nbr.row][nbr.col] = = 0) { //該方格未標(biāo)記
grid[nbr.row][nbr.col] = grid[here.row][here.col] + 1;
if ((nbr.row = = finish.row) && (nbr.col = = finish.col)) break;//完成布線
Q.Add(nbr);
}
}
if ((nbr.row = = finishi.row) && (nbr.col = = finish.col)) break;//完成布線
if (Q.IsEmpty()) //活隊(duì)列是否為空
return 0; //無解
Q.delete(here); //取下一個(gè)擴(kuò)展結(jié)點(diǎn)
}while (1);
//構(gòu)造最短布線路徑
PathLen = grid[finish.row][finish.col] - 2;
path = new Position[PathLen];
here = finish;
for (int j = PathLen – 1; j >= 0; j--) { //找前驅(qū)位置
path[j] = here;
for (i = 0; i< NumOfNbrs; 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; //向前移動(dòng)
}
return 1;
}
void main ()
{
int grid[8][8];
int PathLen, *path;
Position start, finish;
start.row = 3; start.col = 2;
finish.row = 4; finish.col = 6;
FindPath (start, finish, PathLen, path);
}
好了,問題解出來了。咦,我們用的是什么方法呢?呵呵,對(duì),這就是分支限界算法。
算法總結(jié):
分支限界法基本思想:
• 分支限界法常以廣度優(yōu)先或以最小耗費(fèi)(最大效益)優(yōu)先的方式搜索問題的解空間樹。
• 在分支限界法中,每一個(gè)活結(jié)點(diǎn)只有一次機(jī)會(huì)成為擴(kuò)展結(jié)點(diǎn)。活結(jié)點(diǎn)一旦成為擴(kuò)展結(jié)點(diǎn),就一次性產(chǎn)生其所有兒子結(jié)點(diǎn)。
• 在這些兒子結(jié)點(diǎn)中,導(dǎo)致不可行解或?qū)е路亲顑?yōu)解的兒子結(jié)點(diǎn)被舍棄,其余兒子結(jié)點(diǎn)被加入活結(jié)點(diǎn)表中。
• 此后,從活結(jié)點(diǎn)表中取下一結(jié)點(diǎn)成為當(dāng)前擴(kuò)展結(jié)點(diǎn),并重復(fù)上述結(jié)點(diǎn)擴(kuò)展過程。這個(gè)過程一直持續(xù)到找到所需的解或活結(jié)點(diǎn)表為空時(shí)為止。
分支限界法與回溯法的不同:
(1)求解目標(biāo)不同:回溯法的求解目標(biāo)是找出解空間樹中滿足約束條件的所有解,而分支限界法的求解目標(biāo)則是找出滿足約束條件的一個(gè)解,或是在滿足約束條件的解中找出在某種意義下的最優(yōu)解。
(2)搜索方式的不同:回溯法以深度優(yōu)先的方式搜索解空間樹,而分支限界法則以廣度優(yōu)先或以最小耗費(fèi)優(yōu)先的方式搜索解空間樹。
相關(guān)文章
用c語言實(shí)現(xiàn)《狼人殺》游戲發(fā)牌系統(tǒng)
大家好,本篇文章主要講的是用c語言實(shí)現(xiàn)《狼人殺》游戲發(fā)牌系統(tǒng),感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下2022-01-01C語言實(shí)現(xiàn)通訊錄系統(tǒng)課程設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)通訊錄系統(tǒng)課程設(shè)計(jì),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07Opencv基于文字檢測(cè)去圖片水印的實(shí)現(xiàn)示例
去水印是個(gè)麻煩事,本文就來介紹一種方法Opencv基于文字檢測(cè)去圖片水印的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09