先求黑点集直径端点,再用这两个端点去覆盖最远异色距离。
P10725 [GESP202406 八级] 最远点对
GESP8 冲刺课堂页:先建模,再压缩状态,最后读代码。
一、先回答问题
整棵树直径两个端点可能同色,直接输出会错。
二、核心观察
点集直径端点能代表这个点集到任意点的最远距离候选。
为什么不丢答案:树上任一点到点集最远点,一定是该点集直径的某个端点。
B1
B2
最远白点
三、互动演示
步骤1
关键状态 A-
关键状态 B-
答案/目标-
复杂度看表
课堂动作手推
点击下一步开始。
四、自己选答案
为什么不能只求整树直径?
先判断,再点按钮。
B1/B2 是什么?
先判断,再点按钮。
五、手推结果
| BFS 次数 | 目的 |
|---|---|
| 1 | 找黑点集端点 B1 |
| 2 | 找 B2 并更新答案 |
| 3 | 从 B2 更新答案 |
课堂要求:每题先说清对象、关系、保留的信息、压缩掉的信息,再写代码。
六、C++14 参考代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
vector<int> adj[MAXN];
int color[MAXN];
int dist[MAXN];
int n;
// 通用的 BFS,计算从 start 到所有点的距离
void bfs(int start) {
fill(dist, dist + n + 1, -1);
queue<int> q;
q.push(start);
dist[start] = 0;
while (!q.empty()) {
int u = q.front();
q.pop();
for (int v : adj[u]) {
if (dist[v] == -1) {
dist[v] = dist[u] + 1;
q.push(v);
}
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
int first_black = -1;
for (int i = 1; i <= n; i++) {
cin >> color[i];
if (color[i] == 1) first_black = 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);
}
// 1. 找到黑色点集的一个直径端点 B1
bfs(first_black);
int b1 = first_black;
int max_d = -1;
for (int i = 1; i <= n; i++) {
if (color[i] == 1 && dist[i] > max_d) {
max_d = dist[i];
b1 = i;
}
}
// 2. 从 B1 出发,找到另一个直径端点 B2,同时算出 B1 到白点的最大距离
bfs(b1);
int b2 = b1;
int max_d_black = -1;
int ans = 0;
for (int i = 1; i <= n; i++) {
if (color[i] == 1 && dist[i] > max_d_black) {
max_d_black = dist[i];
b2 = i;
}
if (color[i] == 0) {
ans = max(ans, dist[i]);
}
}
// 3. 从 B2 出发,算出 B2 到白点的最大距离
bfs(b2);
for (int i = 1; i <= n; i++) {
if (color[i] == 0) {
ans = max(ans, dist[i]);
}
}
cout << ans << endl;
return 0;
}