迷宫问题的三种实现算法

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<stdio.h>
#include<stdlib.h>
#include<queue>
#include<string.h>
#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<Row;i++)   //读入迷宫信息 
        for (int j=0;j<Col;j++)
           scanf("%d",&Maze[i][j]);
    
    printf("\n");
	printf("Please Set Start Place:\n");

	printf("  Set Start X=");
	scanf("%d",&StartRow);
	
	printf("  Set Start Y=");
	scanf("%d",&StartCol);
	
	printf("\n");
	printf("Please Set End Place:\n");
	
	printf("  Set END   X=");
	scanf("%d",&EndRow);
	
	printf("  Set END   Y=");
	scanf("%d",&EndCol);
	
	printf("\n");
	
}

//递归回溯求一条可行路
void DFS(int NowRow,int NowCol){
	
	Mark[NowRow][NowCol]=1; //标记当前位置
    
    //找到出口,递归中止,开始输出 
	if (NowRow==StartRow&&NowCol==StartCol) { //逆向思维:从终点开始到起点,能够方便输出
		Flag=1;
		printf("(%d,%d)->",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<mindist) {
		    mindist=dist;
			copyresult(); //拷贝结果 
			temp[tempcount].x=IEOF; //清除temp点集 
            temp[tempcount].y=IEOF;
            tempcount--; 
			Mark[NowRow][NowCol]=0; //清除当前标记位置 
		} 
		Flag=1;
		return;
	}
	
	int dRow,dCol;
	
	//探索当前位置周围8个格子有没有能继续走的格子 
	for (dRow=-1;dRow<=1;dRow++)
	    for (dCol=-1;dCol<=1;dCol++)
	        if (CheckOK(NowRow,NowCol,dRow,dCol))  //如果可以继续走
	            MinDFS(NowRow+dRow,NowCol+dCol,dist+1);  //递归至下一个点 
	
	//清除temp点集        
	temp[tempcount].x=IEOF; 
    temp[tempcount].y=IEOF;
    tempcount--;
    
	Mark[NowRow][NowCol]=0;  //清除当前标记位置
	
}

void copyresult(){
	
	int i;
	
	for (i=0;i<=mindist;i++){
		result[i].x=temp[i].x;
		result[i].y=temp[i].y;
	}
	
}

void printresult(){

    if (Flag)
        for (int i=0;i<=mindist;i++)
            printf("Step %d :(%d,%d)\n",i,result[i].x,result[i].y);
	
}

//广度优先搜索求最短路径
void MinBFS(int NowRow,int NowCol){

    queue<struct point> 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];
	    }
	}
	
}



  • 15
    点赞
  • 93
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
迷宫问题是一种经典的寻路问题,可以通过视觉化求解迷宫算法来帮助解决。下面我将通过300字的篇幅来说明。 首先,为了可视化迷宫,我们可以使用计算机图形学相关的库或者工具。通过这些库或工具,我们可以在屏幕上绘制出表示迷宫的图像,用不同的颜色区分墙壁、出口、路径等。 其次,对于求解迷宫算法,我们可以考虑使用深度优先搜索或者广度优先搜索。深度优先搜索算法可以从起点开始,不断探索迷宫中的路径,直到找到出口或者无法前进时回溯。广度优先搜索算法则是从起点开始,并逐层搜索迷宫中可能的路径,直到找到出口。 在可视化求解迷宫算法中,我们可以将搜索过程中的迷宫状态显示在屏幕上。例如,可以将搜索到的路径以不同的颜色在迷宫图像上标记出来,或者在搜索过程中,不断更新迷宫图像的显示,将当前搜索到的位置在图像上进行动态展示。 此外,为了提高求解迷宫算法的效率,我们可以引入一些优化策略。例如,可以使用启发式搜索算法,如A*算法,通过合理地选择搜索路径,减少搜索的时间复杂度。在可视化求解迷宫算法中,我们可以通过在图像上显示搜索路径的优先级或权重,来更好地理解搜索过程的进行。 通过可视化求解迷宫算法,我们可以直观地观察迷宫的结构和搜索的过程,更好地理解算法的执行和路径的选择。同时,可视化还可以帮助我们直观地分析算法的效率和性能,从而进行改进和优化。 综上所述,通过可视化求解迷宫算法,我们可以更好地理解迷宫问题,并提高算法的效率和性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值