資料結構與演算法 二維迷宮問題
問題描述:
設計一個能自動求解給定二維迷宮最短路徑問題的程式,並輸出所有的求解得到的最短路徑和路徑長度。
技術方案:
二維迷宮最短路徑問題
為了表示迷宮,定義一個二維陣列mg,其中的每個元素表示一個方塊的狀態,1表示牆壁,0表示通路。出於演算法實現方便考慮,在迷宮周圍建立了一道圍牆,內部是8*8的迷宮地圖,可以自由編輯,規定迷宮的入口為(1,1),迷宮的出口為(8,8)。
求迷宮問題就是求出從入口到出口的路徑。在求解時,通常用的是“窮舉求解”的方法,即從入口出發,順某一方向向前試探,若能走通,則繼續往前走;否則沿原路退回,換一個方向再繼續試探,直至所有可能的通路都試探完為止。
為了保證在任何位置上都能沿原路退回(稱為回溯),需要用一個後進先出的棧來儲存從入口到當前位置的路徑。
對於迷宮中的每個方塊,有上下左右四個方塊相鄰,第i行第j列的當前方塊的位置為(i,j),規定上方方塊為方位0,順時針方向遞增編號。在試探過程中,假設從方位0到方位3的方向查詢下一個可走的方塊。
為了便於回溯,對於可走的方塊都要進棧,並試探它的下一可走的方位,將這個可走的方位儲存到棧中。
求解迷宮(1,1)到(M,N)路徑的具體過程是:
先將入口進棧(初始方位設定為-1),在棧不空時迴圈:取棧頂方塊(不退棧),若該方塊是出口,則輸出棧中方塊即為路徑。
否則,找下一個可走的相鄰方塊,若不存在這樣的方塊,則退棧。若存在這樣的方塊,則將其方位儲存到棧頂元素中,並將這個可走的相鄰方塊進棧(初始方位設定為-1)。
為了保證試探的可走相鄰方塊不是已走路徑上的方塊,如(i,j)已進棧,在試探(i+1,j)的下一可走方塊時,又試探到(i,j),這樣可能會引起死迴圈,為此,在一個方塊進棧後,將對應的mg陣列元素值改為-1(變為不可走的相鄰方塊),當退棧時(表示沒有可走相鄰方塊),將其恢復為0。
/************************************** Exp_2:二維迷宮問題 Author:Shawn__L Date:2018.5.27 **************************************/ #include "stdafx.h" #include <stdio.h> #define M 8 //行數 #define N 8 //列數 #define MaxSize 100 //棧最多元素個數 struct { int i,j; int di; } Stack[MaxSize],Path[MaxSize]; //定義棧和存放最短路徑的陣列 int top=-1; //棧頂指標 int count=1; //路徑數計數 int minlen=MaxSize; //最短路徑長度 int m,n; int mg[M+2][N+2]; //迷宮的四周為1的牆壁 void init() //建立迷宮 { for(m=0;m<M+2;m++) //建立迷宮四周牆壁 { mg[0][m]=1; mg[M+1][m]=1; mg[m][0]=1; mg[m][N+1]=1; } printf("提示:\n這是一個8*8的迷宮,四周牆壁已經建好,只需要輸入迷宮的內部地圖,1表示牆壁,0表示通路。\n"); printf("迷宮的入口為(1,1),迷宮的出口為(8,8),每個數字以空格分開,每一行輸入8個數字後按回車鍵結束。\n"); for(m=1;m<=M;m++) { printf("\n請輸入第%d行地圖:",m); for(n=1;n<=N;n++) { scanf("%d",&mg[m][n]); } } printf("\n迷宮如下:"); for(m=0;m<M+2;m++) { printf("\n"); for(n=0;n<N+2;n++) { printf("%d ",mg[m][n]); } } } void mgpath(int xi,int yi,int xe,int ye) //求迷宮路徑 { int i,j,di,find,k; top++; //進棧 Stack[top].i=xi; Stack[top].j=yi; Stack[top].di=-1; mg[xi][yi]=-1; //初始方塊進棧 while (top>-1) //棧不空時迴圈 { i=Stack[top].i; j=Stack[top].j; di=Stack[top].di; if (i==xe && j==ye) //找到了出口,輸出路徑 { printf("%d: ",count++); for (k=0;k<=top;k++) { printf("(%d,%d) ",Stack[k].i,Stack[k].j); if ((k+1)%12==0) printf("\n "); //輸出時每12個方塊換一行 } printf("\n"); if (top+1<minlen) //比較找最短路徑 { for (k=0;k<=top;k++) Path[k]=Stack[k]; minlen=top+1; } mg[Stack[top].i][Stack[top].j]=0; //讓該位置變為其他路徑可走方塊 top--; i=Stack[top].i; j=Stack[top].j; di=Stack[top].di; } find=0; while (di<4 && find==0) //找下一個可走方塊 { di++; switch(di) { case 0: i=Stack[top].i-1; j=Stack[top].j; break; case 1: i=Stack[top].i; j=Stack[top].j+1; break; case 2: i=Stack[top].i+1; j=Stack[top].j; break; case 3: i=Stack[top].i; j=Stack[top].j-1; break; } if (mg[i][j]==0) find=1; } if (find==1) //找到了下一個可走方塊 { Stack[top].di=di; //修改原棧頂元素的di值 top++; Stack[top].i=i; Stack[top].j=j; Stack[top].di=-1; //下一個可走方塊進棧 mg[i][j]=-1; //避免重複走到該方塊 } else //沒有路徑可走,則退棧 { mg[Stack[top].i][Stack[top].j]=0; //讓該位置變為其他路徑可走方塊 top--; } } printf("\n最短路徑長度:%d\n",minlen); printf("最短路徑:"); for (k=0;k<minlen;k++) { printf("(%d,%d) ",Path[k].i,Path[k].j); if ((k+1)%12==0) printf("\n\t "); //輸出時每12個方塊換一行 } printf("\n"); } void main() { init(); printf("\n\n迷宮所有路徑如下:\n"); mgpath(1,1,M,N); }