迷宫问题(dfs,bfs)

迷宫问题

定义一个二维数组:

int maze[5][5] = {

0, 1, 0, 0, 0,

0, 1, 0, 1, 0,

0, 0, 0, 0, 0,

0, 1, 1, 1, 0,

0, 0, 0, 1, 0,

};

它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。
Input
一个5 × 5的二维数组,表示一个迷宫。数据保证有唯一解。
Output
左上角到右下角的最短路径,格式如样例所示。
Sample Input
0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0
Sample Output
(0, 0)
(1, 0)
(2, 0)
(2, 1)
(2, 2)
(2, 3)
(2, 4)
(3, 4)
(4, 4)

思路分析:

这是一道很经典的遍历图找最短路问题,遍历图有两种方法:深度优先遍历(dfs)和广度优先遍历(bfs),由于是要寻找最短路,采用bfs效率更高。不过此题的图并不大,用两种方法均不会超时。但此题有一个难点:输出最短路径。这就需要在原来的dfs和bfs模板上加一些存储路径的方法。

bfs解法:

将一个格子看成一个结点,该结点包含三个成员:int x,y,pre。
x和y表示该结点的位置,pre用于指向父结点(即将该结点扩展出来的结点)。当扩展到目标结点(4,4)时,可以通过pre获取到前一结点,最终回到初始结点。由于路径需要正向输出,而我们是从最终结点反推回初始结点,因此需要利用栈(递归)来输出路径。具体代码如下:

#include <cstdio>
#include <algorithm>
using namespace std;

int a[5][5];

typedef struct 
{
	int x;
	int y;
	int pre;		//存放父结点的编号 
}node;

typedef struct 
{
	node data[1000];	//存放结点的队列 
	int front;		//队列头部 
	int rear;		//队列尾部 
}Queue;

void print(Queue qu, int cur)	//获取该队列和当前结点的参数,由于要倒序输出,因此用递归(调用栈) 
{
	if(qu.data[cur].pre == -1)
	{
		printf("(%d, %d)\n",qu.data[cur].x, qu.data[cur].y);
		return;
	}
	print(qu, qu.data[cur].pre);
	printf("(%d, %d)\n",qu.data[cur].x, qu.data[cur].y);
}

void path(int xi, int yi, int xe, int ye)	//从(xi,yi)走到(xe,ye)
{
	int ok = 0; 
	Queue qu;	//创建队列
	qu.rear = 0;	//初始化队列
	qu.front = -1;
	qu.data[0].x = 0;
	qu.data[0].y = 0;
	qu.data[0].pre = -1; 	//初始结点的pre设为-1,作为输出路径的递归出口 
	a[xi][yi] = 2;	//表示该结点已经遍历过 
	while(qu.front != qu.rear && ok == 0)	//若队列不为空 (队列为空表示所有能扩展的结点都扩展完了也没能发现目标结点,表示无解) 
	{
		qu.front++;		//出队,front指向出队结点 
		if(qu.data[qu.front].x == xe && qu.data[qu.front].y == ye)	//若出队的结点是目标结点
		{
			ok = 1;
			//printf("迷宫有解。\n");
			print(qu, qu.front);		//根据该结点反推到初始结点并输出
		} 
		else
		{
			int k;
			int i = qu.data[qu.front].x, j = qu.data[qu.front].y;				 
			for(k = 0;k < 4;k++)	//循环4次,扩展该出队结点 
			{
				i = qu.data[qu.front].x, j = qu.data[qu.front].y;
				switch(k)
				{
					case 0: i--; break;
					case 1: i++; break;
					case 2: j--; break;
					case 3: j++; break;
				}
				if(a[i][j] == 0 && i >= 0 && i <= 4 && j >= 0 && j <= 4)	//扩展结点可用  
				{
					qu.rear++;		//将扩展的结点进队并初始化进队结点的坐标和并将pre设为父结点的编号(之后可通过pre找到父结点) 
					qu.data[qu.rear].x = i, qu.data[qu.rear].y = j;	 
					qu.data[qu.rear].pre = qu.front;	
					a[i][j] == 2; //表示该结点已经遍历过,避免后面重复遍历
				} 
			} 
		} 
	} 
	if(ok == 0) printf("无解。\n");
} 

int main()
{
	int i, j;			//初始化迷宫 
	for(i = 0;i < 5;i++)
	for(j = 0;j < 5;j++)
	scanf("%d",&a[i][j]);
	path(0,0,4,4);
	return 0; 
}

dfs解法:

dfs同样需要将一个格子建成一个结点,该结点包含x,y成员,并建立两个结点数组:path[25], way[25],path用于记录当前所搜的路径,way用于最短路径。因此每搜到一个可行的结点就将该结点存进path数组,由于dfs是按顺序一个一个格子扩展,因此路径可以直接按顺序输出结点数组。由于需要求最短路径,则用一个变量记录步数,若当前遍历的路径比之前搜过的路径步数还要少,就将path数组更新给way数组,最后再将way数组输出即可。具体代码如下:

#include <cstdio>
#include <algorithm>
using namespace std;

int a[5][5], ans = 99999;

typedef struct		//若要记录路径,则应该运用结点数组,每遍历一次,就往结点存当前坐标值,最后按顺序将结点输出即可得到路径 
{
	int x;
	int y;
}node;

node path[25], way[25];		 //path用于存当前路径,way用于存最短路径 

void dfs(node cur, int count)	//状态为当前结点,当前步数 
{
	int i, j, k;
	if(ans > count && cur.x == 4 && cur.y == 4)		//寻到短路径 
	{
		ans = count; 
		for(i = 0;i < count;i++)	//存路径给way 
		{
			way[i].x = path[i].x;
			way[i].y = path[i].y;
		}
	}
	else
	{
		for(k = 0;k < 4;k++)	//遍历4个方向 
		{
			i = cur.x, j = cur.y;
			switch(k)
			{
				case 0: i++; break;
				case 1: i--; break;
				case 2: j++; break;
				case 3: j--; break;
			}
			if(i >= 0 && j >= 0 && i <= 4 && j <= 4 && a[i][j] == 0)	//不超范围,不重复遍历,则扩展的结点可走 
			{
				path[count].x = i;		//给扩展结点存当前坐标值 
				path[count].y = j;
				a[i][j] = 2;			//标示已走路径 
				dfs(path[count], count+1);	//移步到该扩展结点的状态 
				a[i][j] = 0;			//回溯 
			} 
		}
	}
}

int main()
{
	int i, j;
	for(i = 0;i < 5;i++)
	for(j = 0;j < 5;j++)
	scanf("%d",&a[i][j]);
	path[0].x = 0, path[0].y = 0;
	dfs(path[0], 1);
	for(i = 0;i < ans;i++)
	printf("(%d, %d)\n",way[i].x, way[i].y);
	return 0;
}

有其他疑问或建议可在评论区留言。

猜你喜欢

转载自blog.csdn.net/Skyed_blue/article/details/88550952