题解: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

【核心思想】

  1. 问题分析:给定一个 \(n \times m\) 的迷宫矩阵,其中 \(0\) 表示可通行的路,\(1\) 表示墙壁障碍。求从左上角 \((1,1)\) 到右下角 \((n,m)\) 的最短路径长度(最少移动次数)。这是一个BFS(广度优先搜索)问题,因为BFS天然适合求解无权图中的最短路径问题。

  2. 算法选择

    • BFS(广度优先搜索):从起点开始,逐层向外扩展,第一次到达终点时的步数即为最短路径
    • 队列实现:使用队列存储待访问的节点,保证按层次遍历
    • 访问标记:使用 vis 数组或距离数组 d 记录已访问的位置,避免重复访问
  3. 关键步骤

    • 初始化
      • 读入迷宫矩阵 \(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] 即为最短路径长度
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \cdot m)\),每个格子最多被访问一次
    • 空间复杂度:\(O(n \cdot m)\),队列和访问标记数组的空间
  5. 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
posted @ 2026-06-14 10:37  团爸讲算法  阅读(34)  评论(0)    收藏  举报