迷宫问题的三种实现算法

2.走迷宫

2.1 问题描述

给定一个迷宫Maze,并给定迷宫的路口和出口,用递归的方式搜索一条从入口到出口的可行路径(其中红色为围墙,蓝色为可行路),若存在这条路径,则打印出路径,若不存在路径,则输出信息,表示没有路径。

 

 

2.2 问题解决思路和关键点

2.2.1 迷宫在程序中如何表示

因为迷宫中每一个点的信息都只有两种可能(要么能走,要么不能走),我们可以以二维数组Maze[i][j]=value来存储这样一个迷宫,其中用Maze[i][j]表示迷宫坐标的(i,j),并且有0iRow0jCol,而RowCol分别是迷宫的行数与列数。value只有两种值:即010表示当前的格子为红色的墙壁,1表示当前的格子为蓝色的可行路。

2.2.2 迷宫的递归如何实现

迷宫其实有很多的递归实现方法,我们小组主要采取了递归+回溯的算法求迷宫的一条可行路径,同时对迷宫的功能进行了拓展,即递归+回溯+剪枝+记忆化搜索求一条迷宫的最短路径并加以打印,此外我们还写了一种算法作为拓展延伸,虽然它并没有用到递归的实现,但是用了广度优先搜索的算法,同样也能够求出最短路径并输出,在这里我们就不介绍这种广度优先搜索的算法,程序中有相应的注释来介绍该算法。

首先,我们在这里阐述一下求迷宫一条可行路的算法。我们用函数:void BFS (int NowRow int NowCol)来进行迷宫的搜索,两个参量NowRowNowCol分别代表迷宫现在所在格子的行坐标和列坐标,那么递归中止的条件就是NowRow=迷宫终点的行坐标,NowCol=迷宫终点的列坐标。达到了这个条件我们就将全局变量Flag置为1,然后返回上一层递归。既然只要求一条可行路径,那我们如果再加一条if语句来判断Flag的值,如果Flag1那么就直接返回上一层,这样可以在一定程度上减少程序的运行的时间。如果还没有到终点,我们就对它周围的八个格子调用一次CheckOK函数,该函数用于检查其周围的八个格子是否能够走。因此如果能够走,就再调用递归,如此下去直到走到终点或是没有通路为止,此时再返回当前状态,对下一个格子调用递归。

2.2.3 迷宫怎么求最短路

用递归加回溯求迷宫的最短路的值并不难,我们只需要在原有的BFS函数中加入一个参量distvoid MinBFS (int NowRow int NowCol,int dist),参量dist的作用就是代表当前位置与起点的距离,我们起始调用函数时让dist的值为0,即起点距离起点的路径为0,此后我们每调用一次递归,就让dist的值加一即:MinBFS (NextRow NextCol,dist+1),这样就可以保证每一个点的距离都是它上一个点的距离+1。为了求最短距离,我们需要定义一个全局变量mindistmindist的初始值被设为INFINITY(即用宏定义的无穷大),而当迷宫走到终点时,将dist的值与mindist的值做一个比较,如果distmindist,那么我们就将dist的值赋值给mindist并更新最短路。

2.2.4 如何判断迷宫周围的可行路

我们用一个CheckOK函数:int CheckOK (int NowRow,int NowCol,int dRow,int dCol)NowRow表示当前行坐标,NowCol表示当前列坐标,dRow表示下一个格子行坐标的增量,dCol表示下一个格子列坐标的增量,并且-1dRow,dCol1

Check函数中,我们要判断以下五种情况:①下一个格子不能走对角线,并且不能原地走②下一个格子不能行越界③下一个格子不能列越界④如果下一个格子在曾经的搜索中被走过那就不允许再走⑤如果下一个格子是障碍物,那么也不能走。

判断完五种情况后,我们就可以以int的形式返回是否可走,1表示可行,0表示不可行。


#include
#include
#include
#include
#define MaxSize 100
#define INFINITY 100
#define IEOF -1
using namespace std;

int Maze[MaxSize][MaxSize];//初始化迷宫 Maze[i][j]=0表示(i,j)可走, Maze[i][j]=1表示(i,j)不通 
int Mark[MaxSize][MaxSize];//Mark[i][j]=1标记迷宫(i,j)已走过
int distant[MaxSize][MaxSize]; //distant[i][j]表示(i,j)离起始点的最小距离 
int pathrow[MaxSize][MaxSize]; //pathrow[i][j]=prex 表示(i,j)的前驱点的横坐标是prex
int pathcol[MaxSize][MaxSize]; //pathcol[i][j]=prey 表示(i,j)的前驱点的纵坐标是prey

int StartRow,StartCol; 
int EndRow,EndCol;
int Row,Col;
int Flag;//Flag用于判断迷宫是否已经探索到终点 
int mindist; //全局变量,保存路径长度的最小值
int tempcount;

void Init_Variety();
void Init_Window();
void DFS(int NowRow,int NowCol);
int CheckOK(int NowRow,int NowCol,int dRow,int dCol);
void MinDFS(int NowRow,int NowCol,int dist);
void printresult();
void copyresult();
void MinBFS(int NowRow,int NowCol);

//定义解的点集result
struct point{
	int x;
	int y;
};
struct point temp[MaxSize]; //存储当前递归路径 
struct point result[MaxSize]; //存储最短路径 

int main(void){
	
	Init_Window();
	Init_Variety();
	
    
	DFS(EndRow,EndCol);//逆向思维:从终点开始到起点,能够方便输出
	/*
	MinDFS(StartRow,StartCol,0);
	printf("The result of Mindist is %d\n",mindist);
	printresult();
	

    MinBFS(EndRow,EndCol);//逆向思维:从终点开始到起点,能够方便输出
    */
	if (!Flag) printf("NOT FOUND!");

}

//初始化变量 
void Init_Variety(){
	
	Flag=0;
	mindist=INFINITY;
	tempcount=IEOF;
	memset(Maze,0,sizeof(Maze));
	memset(Mark,0,sizeof(Mark));
	
}

//初始化
void Init_Window(){
	
	printf("Set Row=");
	scanf("%d",&Row);
	
	printf("Set Col=");
	scanf("%d",&Col);
	
	printf("\n");
	printf("Please Set the maze:\n");
	
	for (int i=0;i",NowRow,NowCol);
		return;
	}
	
	int dRow,dCol;
	
	//探索当前位置周围8个格子有没有能继续走的格子 
	for (dRow=-1;dRow<=1;dRow++)
	    for (dCol=-1;dCol<=1;dCol++)
	        if (CheckOK(NowRow,NowCol,dRow,dCol)){ //如果可以继续走 
			    DFS(NowRow+dRow,NowCol+dCol); //递归至下一个点 
			    //如果已经走到过终点就直接输出点,然后返回上一层 
			    if (Flag) {
			    	if (NowRow==EndRow&&NowCol==EndCol) //控制输出格式 
			    	    printf("(%d,%d)",NowRow,NowCol);
			    	        else printf("(%d,%d)->",NowRow,NowCol);
		            return;
				}
			}
	
    Mark[NowRow][NowCol]=0;//若没有通路则回溯 

}

//判断是否能走,若能走返回1,否则返回0 
int CheckOK(int NowRow,int NowCol,int dRow,int dCol){
	
	//不能走对角线 
    if (dRow==dCol||dRow==-dCol) return 0;
    //判断是否行越界 
	if (NowRow+dRow<0||NowRow+dRow>=Row) return 0;
	//判断是否列越界 
	if (NowCol+dCol<0||NowCol+dCol>=Col) return 0;
	//判断此路有没有走过,不允许重复走 
	if (Mark[NowRow+dRow][NowCol+dCol]) return 0;
	//判断此路是否通 
	if (Maze[NowRow+dRow][NowCol+dCol]) return 0;
	
	return 1;
	    
}

//回溯剪枝求最一条短路径
void MinDFS(int NowRow,int NowCol,int dist){

    if (dist>=mindist) return; //剪枝:大于等于最小距离后直接返回 
    
    Mark[NowRow][NowCol]=1; //标记当前位置
    
    //将点加入result点集 
    tempcount++;
    temp[tempcount].x=NowRow;
    temp[tempcount].y=NowCol;
    
    //找到出口,保存结果清除当前点后直接返回上一层 
	if (NowRow==EndRow&&NowCol==EndCol) {
		if (dist Q;
    struct point P,R;
    int dr,dc,dx,dy;
    int i,prex,prey,nx,ny;
	
	//清空数组,将所有元素置为-1 
	memset(distant,IEOF,sizeof(distant));
	memset(pathrow,IEOF,sizeof(pathrow));
	memset(pathcol,IEOF,sizeof(pathcol));
	
	distant[NowRow][NowCol]=0; //初始结点的距离定义为0
	P.x=NowRow;
	P.y=NowCol;
	Q.push(P); //将初始结点压入队列 
	
	while(!Q.empty()){
		P=Q.front(); //返回队列第一个元素 
		Q.pop(); //结点P出队 
		dx=P.x;
		dy=P.y;
		for (dr=-1;dr<=1;dr++)
		    for (dc=-1;dc<=1;dc++)
		       if (distant[dx+dr][dy+dc]==IEOF && CheckOK(dx,dy,dr,dc))  { //如果该结点没遍历过并且能够遍历 
		       	  R.x=dx+dr; //定义子结点R 
		       	  R.y=dy+dc;
		   	      distant[dx+dr][dy+dc]=distant[dx][dy]+1; //计算子结点的距离 
		   	      pathrow[dx+dr][dy+dc]=dx; //记入子节点前驱结点的横坐标 
		   	      pathcol[dx+dr][dy+dc]=dy; //记入子节点前驱结点的纵坐标 
		   	      Q.push(R); //将子结点R压入队列 
		       }
	}
	
	
	if (distant[StartRow][StartCol]!=IEOF) {
	    Flag=1; //如果终点被遍历过则Flag置为1
	    printf("The result of mindist is %d\n",distant[StartRow][StartCol]); //输出最短路径长度
		//还原最短路径 
    	prex=StartRow;
	    prey=StartCol;
	    for (i=0;i<=distant[StartRow][StartCol];i++){ //还原前驱结点
		    printf("Step %d :(%d,%d)\n",i,prex,prey);
		    nx=prex;
		    ny=prey;
	    	prex=pathrow[nx][ny];
		    prey=pathcol[nx][ny];
	    }
	}
	
}



你可能感兴趣的:(迷宫问题的三种实现算法)