将交换机和电脑建成一棵树,通过两次 BFS 求树的直径。
OJ: shumeng
题目 ID: CSP201503D
难度:普及-
标签:树BFS树的直径
日期: 2026-07-31 16:21
形式化题目
有
思路
先看一个小数据基准:从每个节点出发做一次 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,时间复杂度为
关键观察
把每台电脑也当作一个节点挂到它连接的交换机上,整个网络仍然是一棵树。题目要求的最大步数就是这棵树的直径。
两次 BFS
无权树求直径的经典做法:
- 从任意节点(例如
)BFS,找到离它最远的节点 ; - 再从
出发 BFS,最远距离就是树的直径。
两步各做一次 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
*/
#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 各遍历一遍所有节点,
,其中 。 - 空间:邻接表存储整棵树,
。
总结
把挂在交换机上的电脑也当作树节点,题目就变成标准树直径问题。两次 BFS 是无权树求直径的线性做法:第一次找到直径一端,第二次从该端出发量出直径长度。

