問題描述:給定一個M X N的迷宮圖,求一條從指定入口的到出口的路徑
數據組織:爲了表示迷宮,設置一個二維數組(migong) mg[ M][N] ,其中數組的每個元素表示一個方塊的狀態,0表示對應的方塊爲通道,1表示對應方塊是牆。爲了算法方便,在迷宮外面加一道牆,所以二維數組表示演變如下:(假設M=8,N=8)
mg[M+2][N+2]=
{
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,0,0,1,1,0,0,1},
{1,0,1,1,1,0,0,0,0,1},
{1,0,0,0,1,0,0,0,0,1},
{1,0,1,0,0,0,1,0,0,1},
{1,0,1,1,1,0,1,1,0,1},
{1,1,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}
};
算法思路:
針對這類問題·,我們通常採用的是“”窮舉法”,即從入口出發,順某一方向向前試探,若能走通,則繼續機組往前走,否則沿原路退回,換另一個方向在試探,直至把所有的路走完爲止。爲了保證在位置上都能沿原路退回(成爲回溯),需要用一個後進先出棧來保存入口到當前位置的路徑。爲了保證試探的可走相鄰方塊不是已走路徑上的方塊,如(i,j)進棧,試探到(i+1,j),接着試探到(i,j),這樣就是引起死循環,爲此,在一個方塊進棧後,將對應mg數組元素值改爲-1,表示該方塊爲不可走相鄰方塊,當該方塊退棧時,將其恢復爲0
下面代碼是給出找到一條路徑的算法:
#include <iostream>
using namespace std;
#define MaxSize 100
#define M 8 //行數
#define N 8 //列數
int mg[M+2][N+2] = {
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,0,0,1,1,0,0,1},
{1,0,1,1,1,0,0,0,0,1},
{1,0,0,0,1,0,0,0,0,1},
{1,0,1,0,0,0,1,0,0,1},
{1,0,1,1,1,0,1,1,0,1},
{1,1,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}
};
typedef struct
{
int row; //當前方塊的行號
int column; //當前方塊的列號
int direction ; //下一個可走的方格的方位編號 0:上 1:右 2:下 3:左
}Box; //定義方塊類型
typedef struct
{
Box data[MaxSize] ;
int top ; //棧頂指針
}StackType ; //定義順序棧類型
bool mgpath(int startRow,int startColumn,int endRow,int endColumn)
{
int i,j,k,direction,find;
StackType stackType ;
stackType.top = -1 ;
stackType.top++;
stackType.data[stackType.top].row = startRow ;
stackType.data[stackType.top].column=startColumn;
stackType.data[stackType.top].direction = -1 ;
mg[startRow][startColumn]= -1 ;
//棧不爲空時循環
while(stackType.top > -1)
{
i=stackType.data[stackType.top].row;
j=stackType.data[stackType.top].column;
direction =stackType.data[stackType.top].direction;
//取棧頂方塊
if(i==endRow && j==endColumn)
{
cout<<"迷宮路徑如下"<<endl;
for(k=0;k<=stackType.top;k++)
{
cout<<"("<<stackType.data[k].row<<","<<stackType.data[k].column<<") " ;
if((k+1)%5 == 0)
cout<<endl;
}
cout<<endl;
return true ;
}
find = 0 ;
//找下一個可走的方塊
while(direction < 4 && find==0)
{
direction++;
switch(direction)
{
case 0 :
i=stackType.data[stackType.top].row-1;
j=stackType.data[stackType.top].column;
break;
case 1 :
i=stackType.data[stackType.top].row;
j=stackType.data[stackType.top].column+1;
break;
case 2:
i=stackType.data[stackType.top].row+1;
j=stackType.data[stackType.top].column;
break;
case 3:
i=stackType.data[stackType.top].row;
j=stackType.data[stackType.top].column-1;
break;
}
if(mg[i][j] == 0)
find = 1 ; //找到下一個可走的相鄰方塊
}
//找到下一個可走方塊
if(find == 1)
{
stackType.data[stackType.top].direction=direction ;
stackType.top++;
stackType.data[stackType.top].row=i ;
stackType.data[stackType.top].column=j ;
stackType.data[stackType.top].direction = -1 ;
mg[i][j]=-1;
}
else
{
//沒有路徑可走,則退棧
mg[stackType.data[stackType.top].row][stackType.data[stackType.top].column] = 0 ;
stackType.top--;
}
}
return false;
}
int main()
{
if(!mgpath(1,1,M,N))
cout<<"該迷宮問題沒有解!"<<endl;
cout<<"結束"<<endl;
return 0 ;
}
運行結果如下:
拓展:輸出所有能從入口到出口的的路徑和其中最短路徑
代碼如下:
#include <iostream>
using namespace std;
#define MaxSize 100
#define M 8 //行數
#define N 8 //列數
int mg[M+2][N+2] = {
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,0,0,1,1,0,0,1},
{1,0,1,1,1,0,0,0,0,1},
{1,0,0,0,1,0,0,0,0,1},
{1,0,1,0,0,0,1,0,0,1},
{1,0,1,1,1,0,1,1,0,1},
{1,1,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}
};
typedef struct
{
int row; //當前方塊的行號
int column; //當前方塊的列號
int direction ; //下一個可走的方格的方位位編號 0:上 1:右 2:下 3:左
}Box; //定義方塊類型
typedef struct
{
Box data[MaxSize] ;
int top ; //棧頂指針
}StackType ; //定義順序棧類型
void mgpath(int startRow,int startColumn,int endRow,int endColumn)
{
int i,j,k,direction,find;
int count=1; //路徑數計數
int minlen=MaxSize; //最短路徑長度
StackType path;
StackType stackType ;
stackType.top = -1 ;
stackType.top++;
stackType.data[stackType.top].row = startRow ;
stackType.data[stackType.top].column=startColumn;
stackType.data[stackType.top].direction = -1 ;
mg[startRow][startColumn]= -1 ;
//棧不爲空時循環
while(stackType.top > -1)
{
i=stackType.data[stackType.top].row;
j=stackType.data[stackType.top].column;
direction =stackType.data[stackType.top].direction;
//取棧頂方塊
if(i==endRow && j==endColumn)
{
count++; //路徑樹基數
cout<<"迷宮路徑如下"<<endl;
for(k=0;k<=stackType.top;k++)
{
cout<<"("<<stackType.data[k].row<<","<<stackType.data[k].column<<") " ;
if((k+1)%5 == 0)
cout<<endl;
}
cout<<endl<<endl;
//因爲空棧top=-1,當棧裏面有元素是爲top=0,1,2,3,4...MaxSize-1 ,總共有MaxSize個元素
//所以stackType.top+1爲棧裏面的元素個數
if(stackType.top+1<minlen)
{
//比較輸出最短路徑
for(k=0;k<=stackType.top;k++)
{
path.data[k]= stackType.data[k];
}
minlen=stackType.top+1;
}
//讓該位置變爲其他路徑的可走結點
mg[stackType.data[stackType.top].row][stackType.data[stackType.top].column] = 0 ;
stackType.top--;
i=stackType.data[stackType.top].row;
j=stackType.data[stackType.top].column;
direction= stackType.data[stackType.top].direction;
}
find = 0 ;
//找下一個可走的方塊
while(direction < 4 && find==0)
{
direction++;
switch(direction)
{
case 0 :
i=stackType.data[stackType.top].row-1;
j=stackType.data[stackType.top].column;
break;
case 1 :
i=stackType.data[stackType.top].row;
j=stackType.data[stackType.top].column+1;
break;
case 2:
i=stackType.data[stackType.top].row+1;
j=stackType.data[stackType.top].column;
break;
case 3:
i=stackType.data[stackType.top].row;
j=stackType.data[stackType.top].column-1;
break;
}
if(mg[i][j] == 0)
find = 1 ; //找到下一個可走的相鄰方塊
}
//找到下一個可走方塊
if(find == 1)
{
stackType.data[stackType.top].direction=direction ;
stackType.top++;
stackType.data[stackType.top].row=i ;
stackType.data[stackType.top].column=j ;
stackType.data[stackType.top].direction = -1 ;
mg[i][j]=-1;
}
else
{
//沒有路徑可走,則退棧
mg[stackType.data[stackType.top].row][stackType.data[stackType.top].column] = 0 ;
stackType.top--;
}
}
cout<<"最短路徑如下:"<<endl;
cout<<"長度:"<<minlen<<endl;
cout<<"路徑:"<<endl;
for(k=0;k<minlen;k++){
cout<<"("<<path.data[k].row<<","<<path.data[k].column<<") " ;
if((k+1)%5==0) //輸出時每5個結點換一行
cout<<endl;
}
cout<<endl;
}
int main()
{
mgpath(1,1,M,N);
cout<<"結束"<<endl;
return 0 ;
}
程序運行結果:
........................................................................................
.......................................................................................
.......................................................................................
讀者可能會奇怪,爲什麼就能確定遍歷完所有路徑?
大家仔細想想,我每個方塊按照(不包括邊框的方塊牆,我們自己加上去的)按照上、右、下、左的方向進行遍歷(代碼中direction代表方向),也就是說,所走的方塊上下左右我都遍歷完了。假設我們可以走的方塊數目爲W個,我們實際上遍歷了4的W次方條可能路徑,在進行剪枝操作,選取可行的路徑。
第一個可行方塊的遍歷順序爲,上,右,下,左。如果第一個可行方塊的上邊有可行方塊,我們將選取1第一方塊的上面方塊做爲第二可行方塊;如果第一方塊上邊沒有可行方塊,將找第一方塊的右方塊,如果是可行方塊,將第一方塊的有方塊做爲第二行方塊,;如果第一方塊的上,右沒有可能方塊,將遍歷第一方塊的下方塊,如果還不是可行方塊,將遍歷第一方塊的左方塊,以此找到第二方塊,第三方塊.....
(此思路和遞歸雷同,相同的案例有:八皇后)