迷宫问题(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;
}

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

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是对DFSBFS和UCS的介绍: DFS(深度优先搜索):总是扩展最深层的节点,使用的是LIFO队列,即使用的是stack栈。DFS在生成节点时做的goal test,因此在搜索树中,DFS总是沿着最深的径搜索,直到找到目标状态或者无法继续搜索为止。DFS的优点是空间复杂度低,但是可能会陷入局部最优解。 BFS(广度优先搜索):总是扩展最浅层的节点,使用的是FIFO队列,即使用的是queue队列。BFS在扩展节点时做的goal test,因此在搜索树中,BFS总是沿着最浅的径搜索,直到找到目标状态或者无法继续搜索为止。BFS的优点是可以找到最优解,但是空间复杂度较高。 UCS(最佳优先搜索):总是扩展当前代价最小的节点,使用的是priority queue优先队列。UCS在扩展节点时做的goal test,因此在搜索树中,UCS总是沿着代价最小的径搜索,直到找到目标状态或者无法继续搜索为止。UCS的优点是可以找到最优解,且可以在frontier集中记录所有合适的解,但是空间复杂度较高。 以下是对DFSBFS和UCS的演示: 假设我们要在一个迷宫中找到从起点到终点的最短径,其中1表示墙,0表示可以通过的迷宫如下所示: ``` 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 0 0 0 0 1 1 0 1 0 1 0 1 1 0 1 1 0 1 0 0 0 0 0 0 1 1 0 1 1 1 1 1 1 0 1 1 0 0 0 0 0 0 0 0 1 1 0 1 1 1 1 1 1 0 1 1 0 0 0 0 0 0 0 0 1 1 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 ``` 我们可以使用DFSBFS和UCS来寻找最短径。其中DFS使用stack栈,BFS使用queue队列,UCS使用priority queue优先队列。具体实现可以参考以下代码: ```python # DFS def dfs(maze, start, end): stack = [(start, [start])] visited = set() while stack: node, path = stack.pop() if node == end: return path if node not in visited: visited.add(node) for neighbor in get_neighbors(maze, node): stack.append((neighbor, path + [neighbor])) return None # BFS def bfs(maze, start, end): queue = [(start, [start])] visited = set() while queue: node, path = queue.pop(0) if node == end: return path if node not in visited: visited.add(node) for neighbor in get_neighbors(maze, node): queue.append((neighbor, path + [neighbor])) return None # UCS def ucs(maze, start, end): queue = [(0, start, [start])] visited = set() while queue: cost, node, path = heapq.heappop(queue) if node == end: return path if node not in visited: visited.add(node) for neighbor in get_neighbors(maze, node): new_cost = cost + 1 heapq.heappush(queue, (new_cost, neighbor, path + [neighbor])) return None # 获取邻居节点 def get_neighbors(maze, node): neighbors = [] row, col = node if row > 0 and maze[row-1][col] == 0: neighbors.append((row-1, col)) if row < len(maze)-1 and maze[row+1][col] == 0: neighbors.append((row+1, col)) if col > 0 and maze[row][col-1] == 0: neighbors.append((row, col-1)) if col < len(maze[0])-1 and maze[row][col+1] == 0: neighbors.append((row, col+1)) return neighbors ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值