网络延时

将交换机和电脑建成一棵树,通过两次 BFS 求树的直径。

OJ: shumeng

题目 ID: CSP201503D

难度:普及-

标签:BFS树的直径

日期: 2026-07-31 16:21

形式化题目

nn 台交换机与 mm 台电脑,交换机按层级组成一棵以 11 号交换机为根的树,每台电脑直接连到某一台交换机上。消息每经过一条连接算一步,问任意两台设备(电脑或交换机)之间传递消息最多需要多少步,即树上所有点对的最大距离(树的直径)。

思路

先看一个小数据基准:从每个节点出发做一次 BFS,统计所有可达距离的最大值。

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-07-31 16:21
 * update_at: 2026-08-17 22:52
 */
// brute.cpp:小数据基准,从每个节点做一次 BFS 求最远距离。
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m;
    cin >> n >> m;
    int total_nodes = n + m;
    vector<vector<int> > graph(total_nodes + 1);
    for (int child = 2; child <= n; child++) {
        int parent;
        cin >> parent;
        graph[parent].push_back(child);
        graph[child].push_back(parent);
    }
    for (int computer = 1; computer <= m; computer++) {
        int parent;
        cin >> parent;
        int node = n + computer;
        graph[parent].push_back(node);
        graph[node].push_back(parent);
    }

    int answer = 0;
    for (int start = 1; start <= total_nodes; start++) {
        vector<int> distance(total_nodes + 1, -1);
        queue<int> q;
        q.push(start);
        distance[start] = 0;
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            answer = max(answer, distance[u]);
            for (int i = 0; i < (int)graph[u].size(); i++) {
                int v = graph[u][i];
                if (distance[v] != -1) continue;
                distance[v] = distance[u] + 1;
                q.push(v);
            }
        }
    }
    cout << answer << '\n';

    return 0;
}

brute.cpp 对每个起点都跑一遍 BFS,时间复杂度为 O(N2)O(N^2),只适合小数据,但逻辑最直接,适合对拍。

关键观察

把每台电脑也当作一个节点挂到它连接的交换机上,整个网络仍然是一棵树。题目要求的最大步数就是这棵树的直径

两次 BFS

无权树求直径的经典做法:

  1. 从任意节点(例如 11)BFS,找到离它最远的节点 uu
  2. 再从 uu 出发 BFS,最远距离就是树的直径。

两步各做一次 BFS,总复杂度为 O(N)O(N)

代码

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-07-31 16:21
 * update_at: 2026-08-17 22:52
 */
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 20005;
vector<int> graph[MAXN]; // 邻接表存树:交换机 1..n,电脑编号为 n+1..n+m

// 从 start 出发 BFS,返回 {最远节点, 到该节点的距离}。
pair<int, int> bfs(int start, int total_nodes) {
    vector<int> distance(total_nodes + 1, -1);
    queue<int> q;
    q.push(start);
    distance[start] = 0;
    int farthest = start;
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        if (distance[u] > distance[farthest]) farthest = u;
        for (int i = 0; i < (int)graph[u].size(); i++) {
            int v = graph[u][i];
            if (distance[v] != -1) continue;
            distance[v] = distance[u] + 1;
            q.push(v);
        }
    }
    return make_pair(farthest, distance[farthest]);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m;
    cin >> n >> m;
    // 交换机按树状结构连接,第 i 台交换机连到上一层的交换机。
    for (int child = 2; child <= n; child++) {
        int parent;
        cin >> parent;
        graph[parent].push_back(child);
        graph[child].push_back(parent);
    }
    // 每台电脑作为新的叶子节点挂在对应的交换机上。
    for (int computer = 1; computer <= m; computer++) {
        int parent;
        cin >> parent;
        int node = n + computer;
        graph[parent].push_back(node);
        graph[node].push_back(parent);
    }

    // 整张网络是一棵树,树的直径 = 两次 BFS:任取一点找最远点,再从该点找最远距离。
    int total_nodes = n + m;
    int endpoint = bfs(1, total_nodes).first;
    cout << bfs(endpoint, total_nodes).second << '\n';

    return 0;
}

复杂度

  • 时间:两次 BFS 各遍历一遍所有节点,O(N)O(N),其中 N=n+mN = n + m
  • 空间:邻接表存储整棵树,O(N)O(N)

总结

把挂在交换机上的电脑也当作树节点,题目就变成标准树直径问题。两次 BFS 是无权树求直径的线性做法:第一次找到直径一端,第二次从该端出发量出直径长度。