题解:洛谷 P2018 消息传递
【题目来源】
【题目描述】
巴蜀国的社会等级森严,除了国王之外,每个人均有且只有一个直接上级,当然国王没有上级。如果 \(A\) 是 \(B\) 的上级,\(B\) 是 \(C\) 的上级,那么 \(A\) 就是 \(C\) 的上级。绝对不会出现这样的关系:\(A\) 是 \(B\) 的上级,\(B\) 也是 \(A\) 的上级。
最开始的时刻是 \(0\),你要做的就是用 \(1\) 单位的时间把一个消息告诉某一个人,让他们自行散布消息。在任意一个时间单位中,任何一个已经接到消息的人,都可以把消息告诉他的一个直接上级或者直接下属。
现在,你想知道:
- 到底需要多长时间,消息才能传遍整个巴蜀国的所有人?
- 要使消息在传递过程中消耗的时间最短,可供选择的人有哪些?
【输入】
输入文件的第一行为一个整数 \(N\)(\(N\le 1000\)),表示巴蜀国人的总数,假如人按照 \(1\) 到 \(n\) 编上了号码,国王的编号是 \(1\)。第 \(2\) 行到第 \(N\) 行(共 \(N-1\) 行),每一行一个整数,第 \(i\) 行的整数表示编号为 \(i\) 的人直接上级的编号。
【输出】
文件输出共计两行:
- 第一行为一个整数,表示最后一个人接到消息的最早时间。
- 第二行有若干个数,表示可供选择人的编号,按照编号从小到大的顺序输出,中间用空格分开。
【输入样例】
8
1
1
3
4
4
4
3
【输出样例】
5
3 4 5 6 7
【算法标签】
普及+# #提高#
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 1005, M = N * 2;
int n;
int h[N], e[M], ne[M], idx; // 邻接表
int cnt[N], ti[N], ans = 1e9, res; // cnt: 子树大小, ti: 每个节点为根的结果, ans: 最小值, res: 当前根的结果
// 添加无向边
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
// 深度优先搜索
void dfs(int u, int fa) // u: 当前节点, fa: 父节点
{
cnt[u] = 1; // 包括自己
int maxn = 0, num = 0, t = 0; // maxn: 最大子树大小, num: 最大子树个数, t: 子节点数
for (int i = h[u]; ~i; i = ne[i]) // 遍历子节点
{
int v = e[i];
if (v == fa) // 跳过父节点
{
continue;
}
dfs(v, u); // 递归处理子树
t++; // 子节点计数
if (cnt[v] > maxn) // 找到更大的子树
{
maxn = cnt[v];
num = 1;
}
else if (cnt[v] == maxn) // 相同大小的子树
{
num++;
}
}
// if (fa) t++; // 如果考虑父节点方向
t = max(t, maxn + num - 1); // 计算所需轮数
cnt[u] += t; // 更新当前节点的完成时间
res = max(res, cnt[u]); // 更新全局最大值
}
int main()
{
cin >> n; // 输入节点数
memset(h, -1, sizeof(h));
for (int i = 2; i <= n; i++) // 输入边
{
int x;
cin >> x;
add(i, x), add(x, i); // 建树
}
for (int i = 1; i <= n; i++) // 以每个节点为根计算
{
memset(cnt, 0, sizeof(cnt)); // 重置计数数组
res = 0; // 重置结果
dfs(i, 0); // 从节点i开始DFS
ti[i] = res; // 记录以i为根的结果
ans = min(ans, ti[i]); // 更新最小值
}
cout << ans << endl; // 输出最小结果
for (int i = 1; i <= n; i++) // 输出所有达到最小值的根节点
{
if (ti[i] == ans)
{
cout << i << " ";
}
}
cout << endl;
return 0;
}
【运行结果】
8
1
1
3
4
4
4
3
5
3 4 5 6 7
浙公网安备 33010602011771号