只保留异色相邻边,问题变成求森林中最长路径的节点数。
P11251 [GESP202409 八级] 美丽路径
GESP8 冲刺课堂页:先建模,再压缩状态,最后读代码。
一、先回答问题
直接求原树直径会经过同色相邻边,不一定是美丽路径。
二、核心观察
对每个点维护向下最长和次长的美丽链长度。
为什么不丢答案:经过一个点的最长路径只能由两条不同子方向的最长美丽链拼起来。
颜色
d1/d2
答案
三、互动演示
步骤1
关键状态 A-
关键状态 B-
答案/目标-
复杂度看表
课堂动作手推
点击下一步开始。
四、自己选答案
长度按什么计算?
先判断,再点按钮。
同色边怎么处理?
先判断,再点按钮。
五、手推结果
| 状态 | 意义 |
|---|---|
| d1 | 向下最长美丽链 |
| d2 | 向下次长美丽链 |
| d1+d2+1 | 经过 u 的答案 |
课堂要求:每题先说清对象、关系、保留的信息、压缩掉的信息,再写代码。
六、C++14 参考代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
vector<int> adj[MAXN];
int color[MAXN];
bool visited[MAXN];
int ans = 0;
// d1 表示从 u 向下延伸的最长美丽路径(边数)
// d2 表示从 u 向下延伸的次长美丽路径(边数)
int dfs(int u, int p) {
visited[u] = true;
int d1 = 0, d2 = 0;
for (int v : adj[u]) {
if (v == p) continue;
// 只有颜色不同才能继续延伸美丽路径
if (color[u] != color[v]) {
int d = dfs(v, u);
if (d > d1) {
d2 = d1;
d1 = d;
} else if (d > d2) {
d2 = d;
}
} else {
// 颜色相同,虽然不延伸当前路径,但也要访问 v 确保处理所有连通块
if (!visited[v]) {
dfs(v, u);
}
}
}
// 更新全局最长路径(节点数 = 边数 + 1)
ans = max(ans, d1 + d2 + 1);
// 返回以 u 为顶点的最长向下路径长度(节点数)
return d1 + 1;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
if (n == 0) {
cout << 0 << endl;
return 0;
}
for (int i = 1; i <= n; i++) {
cin >> color[i];
}
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}
ans = 0;
for (int i = 1; i <= n; i++) {
if (!visited[i]) {
dfs(i, 0);
}
}
cout << ans << endl;
return 0;
}