题解:AcWing 844 走迷宫
【题目来源】
AcWing:844 走迷宫 - AcWing题库
【题目描述】
给定一个 \(n\times m\) 的二维整数数组,用来表示一个迷宫,数组中只包含 \(0\) 或 \(1\),其中 \(0\) 表示可以走的路,\(1\) 表示不可通过的墙壁。
最初,有一个人位于左上角 \((1,1)\) 处,已知该人每次可以向上、下、左、右任意一个方向移动一个位置。
请问,该人从左上角移动至右下角 \((n,m)\) 处,至少需要移动多少次。
数据保证 \((1,1)\) 处和 \((n,m)\) 处的数字为 \(0\),且一定至少存在一条通路。
【输入】
第一行包含两个整数 \(n\) 和 \(m\)。
接下来 \(n\) 行,每行包含 \(m\) 个整数(\(0\) 或 \(1\)),表示完整的二维数组迷宫。
【输出】
输出一个整数,表示从左上角移动至右下角的最少移动次数。
【输入样例】
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
【输出样例】
8
【核心思想】
-
问题分析:给定一个 \(n \times m\) 的迷宫矩阵,其中 \(0\) 表示可通行的路,\(1\) 表示墙壁障碍。求从左上角 \((1,1)\) 到右下角 \((n,m)\) 的最短路径长度(最少移动次数)。这是一个BFS(广度优先搜索)问题,因为BFS天然适合求解无权图中的最短路径问题。
-
算法选择:
- BFS(广度优先搜索):从起点开始,逐层向外扩展,第一次到达终点时的步数即为最短路径
- 队列实现:使用队列存储待访问的节点,保证按层次遍历
- 访问标记:使用
vis数组或距离数组d记录已访问的位置,避免重复访问
-
关键步骤:
- 初始化:
- 读入迷宫矩阵 \(g[n][m]\)
- 初始化距离数组
d为 \(-1\)(表示未访问),起点d[0][0] = 0 - 将起点加入队列
- BFS主循环(队列非空时):
- 取出队首节点 \((x, y)\)
- 遍历四个方向(上、右、下、左):
- 计算新坐标 \((nx, ny) = (x + dx[i], y + dy[i])\)
- 检查新坐标是否合法:在边界内、不是障碍、未访问
- 更新距离:
d[nx][ny] = d[x][y] + 1 - 将新坐标加入队列
- 返回结果:
d[n-1][m-1]即为最短路径长度
- 初始化:
-
时间/空间复杂度:
- 时间复杂度:\(O(n \cdot m)\),每个格子最多被访问一次
- 空间复杂度:\(O(n \cdot m)\),队列和访问标记数组的空间
-
BFS的核心思想:
- 层次遍历:从起点开始,先访问所有距离为 \(1\) 的节点,再访问距离为 \(2\) 的节点,以此类推
- 最短路径保证:由于按层次扩展,第一次到达终点时一定是最短路径
- 队列特性:先进先出(FIFO),保证先访问的节点先扩展
- 访问标记:避免重复访问,防止死循环
- 方向数组:使用
dx[4]和dy[4]简化四个方向的遍历 - 适用于无权图的最短路径、连通性判断、层次遍历类问题
【解题思路】

【算法标签】
BFS-二维
【代码详解】
// 使用STL队列
#include <bits/stdc++.h>
using namespace std;
const int N = 110; // 定义地图最大尺寸
// 定义节点结构体,存储位置和步数
struct Node
{
int x; // 当前节点的x坐标
int y; // 当前节点的y坐标
int step; // 从起点到当前节点的步数
};
int n, m; // 地图的行数和列数
int g[N][N]; // 存储地图信息(0表示可通行,1表示障碍)
bool vis[N][N]; // 标记数组,记录节点是否被访问过
int dx[4] = {-1, 0, 1, 0}; // 四个方向的x偏移量(上、右、下、左)
int dy[4] = {0, 1, 0, -1}; // 四个方向的y偏移量(上、右、下、左)
queue<Node> q; // BFS使用的队列
// 检查坐标(x,y)是否合法且可通行
bool check(int x, int y)
{
// 检查是否越界
if (x < 1 || x > n || y < 1 || y > m)
return false;
// 检查是否是障碍物
if (g[x][y] == 1)
return false;
// 检查是否已被访问
if (vis[x][y])
return false;
return true;
}
// BFS算法实现,返回从起点到终点的最短步数
int bfs(int x, int y, int step)
{
// 标记起点为已访问
vis[x][y] = true;
// 将起点加入队列
q.push(Node{x, y, step});
// 当队列不为空时循环
while (!q.empty())
{
// 取出队首节点
Node nownode = q.front();
q.pop();
// 如果到达终点,返回当前步数
if (nownode.x == n && nownode.y == m)
{
return nownode.step;
}
// 遍历四个方向
for (int i = 0; i < 4; i++)
{
int nx = nownode.x + dx[i]; // 计算新位置的x坐标
int ny = nownode.y + dy[i]; // 计算新位置的y坐标
// 如果新位置合法且未被访问
if (check(nx, ny))
{
vis[nx][ny] = true; // 标记为已访问
// 将新节点加入队列,步数+1
q.push(Node{nx, ny, nownode.step + 1});
}
}
}
// 如果无法到达终点,返回-1
return -1;
}
int main()
{
// 输入地图尺寸
cin >> n >> m;
// 输入地图数据
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> g[i][j];
// 调用BFS并输出结果
cout << bfs(1, 1, 0) << endl;
return 0;
}
// 数组模拟队列(优先推荐)
#include <bits/stdc++.h>
using namespace std;
const int N = 110; // 定义地图最大尺寸
typedef pair<int, int> PII; // 定义坐标对类型
int n, m; // 地图的行数和列数
int g[N][N]; // 存储地图信息(0表示可通行,1表示障碍)
int d[N][N]; // 存储每个位置到起点的最短距离
PII q[N * N]; // 数组模拟的队列,存储待处理的坐标
// BFS算法实现,返回从起点到终点的最短步数
int bfs()
{
int hh = 0, tt = 0; // 队列头指针和尾指针
q[0] = {0, 0}; // 将起点(0,0)加入队列
memset(d, -1, sizeof(d)); // 初始化距离数组为-1(表示未访问)
d[0][0] = 0; // 起点到自己的距离为0
// 定义四个方向的偏移量(上、右、下、左)
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};
// 当队列不为空时循环
while (hh <= tt)
{
auto t = q[hh++]; // 取出队首元素
// 遍历四个方向
for (int i = 0; i < 4; i++)
{
int x = t.first + dx[i]; // 计算新位置的x坐标
int y = t.second + dy[i]; // 计算新位置的y坐标
// 检查新位置是否合法且未被访问
if (x >= 0 && x < n && y >= 0 && y < m && g[x][y] == 0 && d[x][y] == -1)
{
d[x][y] = d[t.first][t.second] + 1; // 更新距离
q[++tt] = {x, y}; // 将新位置加入队列
}
}
}
return d[n - 1][m - 1]; // 返回终点到起点的距离
}
int main()
{
// 输入地图尺寸
cin >> n >> m;
// 输入地图数据
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
cin >> g[i][j];
// 调用BFS并输出结果
cout << bfs() << endl;
return 0;
}
#include <bits/stdc++.h>
using namespace std;
const int N = 110; // 地图最大行列数
int g[N][N]; // 地图矩阵,0表示可通行,1表示障碍
int n, m; // n: 行数, m: 列数
// 节点结构体,存储位置和步数
struct Node
{
int row, col; // 当前节点的行列坐标
int step; // 从起点到当前节点的步数
};
queue<Node> Q; // BFS队列
// 方向数组:上、右、下、左
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};
bool Visit[N][N]; // 访问标记数组,记录已访问的位置
/**
* 检查位置(x,y)是否可以访问
* @param x 行坐标
* @param y 列坐标
* @return true表示可以访问,false表示不可访问
*/
bool check(int x, int y)
{
// 检查是否越界
if (x < 1 || x > n || y < 1 || y > m)
return false;
// 检查是否是障碍物
if (g[x][y] == 1)
return false;
// 检查是否已访问过
return !Visit[x][y];
}
/**
* 广度优先搜索(BFS)寻找最短路径
* 从起点(x,y)开始,到终点(n,m)的最短步数
* @param x 起点的行坐标
* @param y 起点的列坐标
* @param step 初始步数(通常为0)
* @return 到达终点的最短步数,无法到达返回-1
*/
int bfs(int x, int y, int step)
{
// 标记起点为已访问
Visit[x][y] = true;
// 起点入队
Q.push(Node{x, y, step});
// BFS主循环
while (!Q.empty())
{
// 取出队首节点
Node nownode = Q.front();
// 检查是否到达终点
if (nownode.row == n && nownode.col == m)
{
return nownode.step; // 返回步数
}
// 向四个方向扩展
for (int k = 0; k < 4; k++)
{
// 计算新位置
int nx = nownode.row + dx[k];
int ny = nownode.col + dy[k];
// 检查新位置是否可访问
if (check(nx, ny))
{
// 标记新位置为已访问
Visit[nx][ny] = true;
// 新节点入队,步数加1
Q.push(Node{nx, ny, nownode.step + 1});
}
}
// 当前节点处理完成,出队
Q.pop();
}
// 队列为空仍未到达终点,返回-1
return -1;
}
int main()
{
// 初始化访问标记数组
memset(Visit, false, sizeof(Visit));
// 输入地图行列数
cin >> n >> m;
// 读入地图数据
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cin >> g[i][j];
}
}
// 从起点(1,1)开始BFS,初始步数为0
int result = bfs(1, 1, 0);
// 输出结果
cout << result << endl;
return 0;
}
【运行结果】
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
8
浙公网安备 33010602011771号