题解:AcWing 847 图中点的层次

【题目来源】

AcWing:847 图中点的层次 - AcWing题库

【题目描述】

给定一个\(n\)个点\(m\)条边的有向图,图中可能存在重边和自环。所有边的长度都是\(1\),点的编号为\(1\sim n\)。请你求出\(1\)号点到\(n\)号点的最短距离,如果从\(1\)号点无法走到\(n\)号点,输出\(-1\)

【输入】

第一行包含两个整数\(n\)\(m\)

接下来\(m\)行,每行包含两个整数\(a\)\(b\),表示存在一条从\(a\)走到\(b\)的长度为\(1\)的边。

【输出】

输出一个整数,表示\(1\)号点到\(n\)号点的最短距离。

【输入样例】

4 5
1 2
2 3
3 4
1 3
1 4

【输出样例】

1

【核心思想】

  1. 问题分析:给定 \(n\) 个点 \(m\) 条边的有向无权图,所有边长度为 \(1\),求从 \(1\) 号点到 \(n\) 号点的最短距离。这是一个经典的BFS求无权图最短路径问题,关键在于BFS按层扩展的特性保证第一次到达目标即为最短距离。

  2. 算法选择

    • BFS(广度优先搜索):按层遍历图,第 \(k\) 层节点距离起点为 \(k\)
    • 邻接表(链式前向星):高效存储图结构,支持重边和自环
    • 距离数组 d:记录从起点到每个节点的最短距离,\(-1\) 表示未访问
    • 访问标记数组 st:避免重复访问节点
  3. 关键步骤

    • 建图:初始化邻接表,读入 \(m\) 条有向边
    • BFS初始化:起点 \(1\) 入队,标记为已访问,d[1] = 0
    • BFS扩展
      • 取出队首节点 \(t\)
      • 遍历 \(t\) 的所有邻接节点 \(j\)
      • \(j\) 未访问,更新 d[j] = d[t] + 1,标记为已访问,入队
    • 返回答案:返回 d[n],若仍为 \(-1\) 表示不可达
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n + m)\),每个节点和边只被访问一次
    • 空间复杂度:\(O(n + m)\),邻接表、距离数组和队列
  5. BFS求最短路径的核心思想

    • 按层扩展:BFS从起点开始,先访问距离为 \(1\) 的节点,再访问距离为 \(2\) 的节点,以此类推
    • 第一次访问即最短:由于边权相同,第一次访问某个节点时的距离即为最短距离
    • 无权图最优:对于所有边权重相同的图,BFS是求最短路径的最优算法
    • 适用于无权图或边权相同的图的最短路径问题

【解题思路】

【算法标签】

BFS-图

【代码详解】

// 使用STL队列
#include <bits/stdc++.h>
using namespace std;

const int N = 100010;  // 节点最大数量
int n, m;  // n: 节点数量,m: 边数量
int h[N], e[N], ne[N], idx;  // 邻接表存储图,h: 头节点数组,e: 边终点数组,ne: 下一条边数组,idx: 边计数器
int d[N];  // 记录从起点到每个节点的距离
bool st[N];  // 记录节点是否被访问过
queue<int> q;  // BFS队列

// 添加一条有向边 a->b
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

// 广度优先搜索,返回从节点1到节点n的最短距离
int bfs()
{
    st[1] = true;  // 标记起点为已访问
    q.push(1);  // 起点入队
    memset(d, -1, sizeof(d));  // 初始化距离为-1(表示不可达)
    d[1] = 0;  // 起点距离为0

    while (!q.empty())
    {
        int t = q.front();  // 取出队首节点
        q.pop();

        // 遍历当前节点的所有邻接节点
        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];  // 邻接节点编号
            if (!st[j])  // 如果邻接节点未被访问
            {
                st[j] = true;  // 标记为已访问
                d[j] = d[t] + 1;  // 更新距离
                q.push(j);  // 入队
            }
        }
    }
    return d[n];  // 返回到达节点n的距离
}

int main()
{
    cin >> n >> m;  // 读入节点数量和边数量
    memset(h, -1, sizeof(h));  // 初始化邻接表头节点

    // 读入边并建图
    for (int i = 0; i < m; i++)
    {
        int a, b;
        cin >> a >> b;
        add(a, b);
    }

    cout << bfs() << endl;  // 输出从1到n的最短距离
    return 0;
}
// 数据模拟队列
#include <bits/stdc++.h>
using namespace std;

const int N = 100010;  // 节点最大数量
int n, m;  // n: 节点数量,m: 边数量
int h[N], e[N], ne[N], idx;  // 邻接表存储图,h: 头节点数组,e: 边终点数组,ne: 下一条边数组,idx: 边计数器
int d[N], q[N];  // d: 记录从起点到每个节点的距离,q: 数组模拟队列

// 添加一条有向边 a->b
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

// 广度优先搜索,返回从节点1到节点n的最短距离
int bfs()
{
    int hh = 0, tt = 0;  // 队列头尾指针
    q[0] = 1;  // 起点入队
    memset(d, -1, sizeof(d));  // 初始化距离为-1(表示不可达)
    d[1] = 0;  // 起点距离为0

    while (hh <= tt)  // 队列不为空
    {
        int t = q[hh++];  // 取出队首节点

        // 遍历当前节点的所有邻接节点
        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];  // 邻接节点编号
            if (d[j] == -1)  // 如果邻接节点未被访问
            {
                d[j] = d[t] + 1;  // 更新距离
                q[++tt] = j;  // 入队
            }
        }
    }
    return d[n];  // 返回到达节点n的距离
}

int main()
{
    cin >> n >> m;  // 读入节点数量和边数量
    memset(h, -1, sizeof(h));  // 初始化邻接表头节点

    // 读入边并建图
    for (int i = 0; i < m; i++)
    {
        int a, b;
        cin >> a >> b;
        add(a, b);
    }

    cout << bfs() << endl;  // 输出从1到n的最短距离
    return 0;
}
#include <bits/stdc++.h>
using namespace std;

const int N = 100010; // 定义常量 N,用于数组大小
vector<int> h[N];     // 邻接表存储图
int d[N];             // 存储每个节点到起点的距离
queue<int> q;         // BFS 使用的队列
int n, m;             // n 是节点数量,m 是边的数量

// BFS 函数,从起点 u 开始遍历图
void bfs(int u) {
    d[u] = 0; // 起点到自身的距离为 0
    q.push(u); // 将起点加入队列

    while (!q.empty()) { // 当队列不为空时
        int nd = q.front(); // 取出队首节点
        q.pop(); // 弹出队首节点

        // 遍历当前节点的所有邻接节点
        for (int i = 0; i < h[nd].size(); i++) {
            int j = h[nd][i]; // 邻接节点 j
            if (d[j] == -1) {  // 如果节点 j 未被访问过
                d[j] = d[nd] + 1; // 更新节点 j 的距离
                q.push(j); // 将节点 j 加入队列
            }
        }
    }
}

int main() {
    memset(d, -1, sizeof(d)); // 初始化距离数组为 -1,表示未被访问
    cin >> n >> m; // 读取节点数量 n 和边的数量 m

    // 读取边的信息并构建图
    for (int i = 1; i <= m; i++) {
        int a, b;
        cin >> a >> b; // 读取边的两个端点
        h[a].push_back(b); // 添加边 a -> b
    }

    bfs(1); // 从节点 1 开始 BFS

    cout << d[n]; // 输出节点 n 到节点 1 的最短距离
    return 0;
}

【运行结果】

4 5
1 2
2 3
3 4
1 3
1 4
1
posted @ 2026-06-14 10:37  团爸讲算法  阅读(12)  评论(0)    收藏  举报