1. 程式人生 > >資料結構與演算法 二維迷宮問題

資料結構與演算法 二維迷宮問題

問題描述:

設計一個能自動求解給定二維迷宮最短路徑問題的程式,並輸出所有的求解得到的最短路徑和路徑長度。

技術方案:

二維迷宮最短路徑問題
為了表示迷宮,定義一個二維陣列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);
}