因为迷宫中每一个点的信息都只有两种可能(要么能走,要么不能走),我们可以以二维数组Maze[i][j]=value来存储这样一个迷宫,其中用Maze[i][j]表示迷宫坐标的(i,j),并且有0≤i<Row、0≤j<Col,而Row和Col分别是迷宫的行数与列数。value只有两种值:即0或1,0表示当前的格子为红色的墙壁,1表示当前的格子为蓝色的可行路。
迷宫其实有很多的递归实现方法,我们小组主要采取了递归+回溯的算法求迷宫的一条可行路径,同时对迷宫的功能进行了拓展,即递归+回溯+剪枝+记忆化搜索求一条迷宫的最短路径并加以打印,此外我们还写了一种算法作为拓展延伸,虽然它并没有用到递归的实现,但是用了广度优先搜索的算法,同样也能够求出最短路径并输出,在这里我们就不介绍这种广度优先搜索的算法,程序中有相应的注释来介绍该算法。
首先,我们在这里阐述一下求迷宫一条可行路的算法。我们用函数:void BFS (int NowRow int NowCol)来进行迷宫的搜索,两个参量NowRow和NowCol分别代表迷宫现在所在格子的行坐标和列坐标,那么递归中止的条件就是NowRow=迷宫终点的行坐标,NowCol=迷宫终点的列坐标。达到了这个条件我们就将全局变量Flag置为1,然后返回上一层递归。既然只要求一条可行路径,那我们如果再加一条if语句来判断Flag的值,如果Flag为1那么就直接返回上一层,这样可以在一定程度上减少程序的运行的时间。如果还没有到终点,我们就对它周围的八个格子调用一次CheckOK函数,该函数用于检查其周围的八个格子是否能够走。因此如果能够走,就再调用递归,如此下去直到走到终点或是没有通路为止,此时再返回当前状态,对下一个格子调用递归。
用递归加回溯求迷宫的最短路的值并不难,我们只需要在原有的BFS函数中加入一个参量dist:void MinBFS (int NowRow int NowCol,int dist),参量dist的作用就是代表当前位置与起点的距离,我们起始调用函数时让dist的值为0,即起点距离起点的路径为0,此后我们每调用一次递归,就让dist的值加一即:MinBFS (NextRow NextCol,dist+1),这样就可以保证每一个点的距离都是它上一个点的距离+1。为了求最短距离,我们需要定义一个全局变量mindist,mindist的初始值被设为INFINITY(即用宏定义的无穷大),而当迷宫走到终点时,将dist的值与mindist的值做一个比较,如果dist<mindist,那么我们就将dist的值赋值给mindist并更新最短路。
我们用一个CheckOK函数:int CheckOK (int NowRow,int NowCol,int dRow,int dCol),NowRow表示当前行坐标,NowCol表示当前列坐标,dRow表示下一个格子行坐标的增量,dCol表示下一个格子列坐标的增量,并且-1≤dRow,dCol≤1。
在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];
}
}
}