迷宫的最短路径

题目来源

挑战程序设计竞赛(第二版)

语言

C/C++

题目

迷宫的最短路径

描述

给定一个大小为 N * M 的迷宫。迷宫由通道和墙壁组成,每一步可以向邻接的上下左右四格的通道移动。请求出从起点到终点所需要的最小步数。请注意,本题假定从起点一定可以移动到终点。

样例

输入:

N=10,M=10(迷宫如下所示。'#', '.', 'S', 'G' 分别表示墙壁、通道、起点和终点)

# S # # # # # # .  #

.  .  .  .  .  .  # .  .  #

.  # .  # # .  # # .  #
.  # .  .  .  .  .  .  .  .
# # .  # # .  # # # #

.  .  .  .  # .  .  .  .  #
.  # # # # # # # .  #
.  .  .  .  # .  .  .  .  .
.  # # # # .  # # # .

.  .  .  .  # .  .  . G #

输出:

22

思路

该题求最短路径,一般求最短路径、最少操作之类的题都应该想到宽度优先搜索(BFS),因为宽度优先搜索每次对当前结点所有可能达到的位置都遍历一遍,很符合最少的思想。尝试了一下宽度优先搜索,发现果然可以,宽度优先搜索的实现用到了队列的思想。

 

设置一个二维数组表示距离,用来记录每一步能到达的位置,用无穷大将数组初始化,代表刚开始所有的位置都无法到达。开始时将起点距离设置为 0,进行宽度优先搜索,最后检查终点位置的数值,若为无穷大则不能到达,否则该值为起点到终点的最短距离。

代码

#include <iostream>
#include <queue>
#define MAX_N 100
#define MAX_M 100 

using namespace std;

const int INF = 1000000;	// 无穷大

// input
char maze[MAX_N][MAX_M] = {
	"#S######.#",
	"......#..#",
	".#.##.##.#",
	".#........",
	"##.##.####",
	"....#....#",
	".#######.#",
	"....#.....",
	".####.###.",
	"....#...G#",
}; 
int M = 10, N = 10;
int sx = 0, sy = 1;	// 起点坐标 
int gx = 9, gy = 8;	// 终点坐标 
// input end 

// code 
 struct point{	// 坐标 
	int x;
	int y;
};
int d[MAX_N][MAX_M];	// 到各个位置的最短距离的数组 
int dx[4] = {1, 0, -1, 0};	// 下、右、上、左 
int dy[4] = {0, 1, 0, -1};

int bfs();

int main() {
	int len = bfs();
	cout << len << endl;

	
	return 0;
}

// 求从 <sx, sy> 到 <gx, gy> 的最短距离
// 如果无法到达,则是 INF 
int bfs()
{
	queue<point> que;
	// 把所有位置都初始化为 INF
	for (int i = 0; i < N; i++){
		for (int j = 0; j < M; j++){
			d[i][j] = INF;
		} 
	} 
	// 把起点加入队列,并把这一地点的距离设置为 0
	point p;
	p.x = sx;
	p.y = sy;
	que.push(p);
	d[sx][sy] = 0;
	
	// 不断循环直到队列长度为 0 
	while (que.size()){
		// 从队列前端取出元素
		point p_tem = que.front();
		que.pop();
		// 如果取出的状态已经是终点,则结束搜索
		if (p_tem.x == gx && p_tem.y == gy)
			break;
		// 四个方向循环
		for (int i = 0; i < 4; i++){
			// 移动之后的位置记为 <nx, ny> 
			int nx = p_tem.x + dx[i];
			int ny = p_tem.y + dy[i];
			// 判断是否可以移动以及是否已经访问过(d[nx][ny] != INF 即为访问过)
			if (maze[nx][ny] != '#' && nx >= 0 && nx < N
				 && ny >= 0 && ny < M && d[nx][ny] == INF){
				 	// 可以移动的话,则加入队列,并且到该位置的距离确定为到 p 的
					//  距离 +1
					point p_add;
					p_add.x = nx;
					p_add.y = ny;
					que.push(p_add);
					d[nx][ny] = d[p_tem.x][p_tem.y] + 1;
				 } 
		} 	 
	} 
	return d[gx][gy];
} 

时间复杂度0

最坏情况对整个 N * M 的数组遍历一次,因此时间复杂度为 O(N * M)。

所涉及的知识点

宽度优先搜索(BFS)

### BFS算法实现迷宫问题的最短路径 BFS(广度优先搜索)是一种逐层扩展的搜索策略,其核心在于利用队列来存储待访问节点的信息。对于迷宫问题中的最短路径求解,BFS能够保证首次抵达目标节点时所经过的路径是最短的[^1]。 以下是基于Python语言的一个典型实现: #### 输入定义 假设迷宫是一个二维数组 `maze`,其中: - `0` 表示可以通过的位置; - `1` 表示墙壁或不可通过的位置; - 起点坐标为 `(start_x, start_y)`; - 终点坐标为 `(end_x, end_y)`。 #### 输出定义 返回从起点到终点的最短路径长度。如果没有可行路径,则返回 `-1`。 ```python from collections import deque def bfs_shortest_path(maze, start_x, start_y, end_x, end_y): rows, cols = len(maze), len(maze[0]) # 如果起始点或结束点超出边界或者被墙挡住,直接返回 -1 if maze[start_x][start_y] != 0 or maze[end_x][end_y] != 0: return -1 visited = [[False for _ in range(cols)] for _ in range(rows)] queue = deque() directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] # 右、下、左、上四个方向移动向量 # 将起点入队并标记已访问 queue.append((start_x, start_y, 0)) # 记录当前坐标和步数 visited[start_x][start_y] = True while queue: current_x, current_y, steps = queue.popleft() # 出队 # 判断是否到达终点 if current_x == end_x and current_y == end_y: return steps # 探索四个方向上的相邻格子 for dx, dy in directions: next_x, next_y = current_x + dx, current_y + dy # 检查新位置是否合法且未访问过 if 0 <= next_x < rows and 0 <= next_y < cols and not visited[next_x][next_y] and maze[next_x][next_y] == 0: queue.append((next_x, next_y, steps + 1)) visited[next_x][next_y] = True # 若遍历结束后仍未找到终点,说明不存在路径 return -1 ``` #### 测试案例 以下是对上述函数的一次测试调用: ```python if __name__ == "__main__": maze = [ [0, 1, 0, 0, 0], [0, 1, 0, 1, 0], [0, 0, 0, 1, 0], [0, 1, 1, 1, 0], [0, 0, 0, 0, 0] ] start_x, start_y = 0, 0 end_x, end_y = 4, 4 result = bfs_shortest_path(maze, start_x, start_y, end_x, end_y) print(f"The shortest path length is {result}") # 应该输出 The shortest path length is 8 ``` 此代码实现了使用BFS解决迷宫最短路径的问题,并能有效处理各种复杂情况下的输入数据[^2]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值