迷宮問題

問題描述:給定一個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第一方塊的上面方塊做爲第二可行方塊;如果第一方塊上邊沒有可行方塊,將找第一方塊的右方塊,如果是可行方塊,將第一方塊的有方塊做爲第二行方塊,;如果第一方塊的上,右沒有可能方塊,將遍歷第一方塊的下方塊,如果還不是可行方塊,將遍歷第一方塊的左方塊,以此找到第二方塊,第三方塊.....

(此思路和遞歸雷同,相同的案例有:八皇后)






發佈了33 篇原創文章 · 獲贊 36 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章