給定 n 個節點的樹(邊長為 1),需在兩個節點建立消防站,使得所有節點到最近消防站的最大距離最小。輸出最小最大距離及兩個消防站節點編號。
最優解必定與樹的直徑相關。樹的直徑是樹上最長路徑,其端點通過兩次 BFS/DFS 可求得。直徑中點(或中間邊)是關鍵分割點。
int bfs(int st) {
queue q; q.push(st);
vis[st] = 1; d[st] = 0; fa[st] = -1;
int ans = st;
while (!q.empty()) {
int u = q.front(); q.pop();
for (int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].v;
if (vis[v]) continue;
vis[v] = 1; d[v] = d[u] + 1; fa[v] = u;
if (d[v] > d[ans]) ans = v;
q.push(v);
}
}
// 重構路徑
top = 0; int u = ans;
while (u != -1) { s[top++] = u; u = fa[u]; }
return ans;
}
// 主邏輯
int st = bfs(1), ed = bfs(st);
int depth = d[ed] + 1;
int l = s[depth/2 - 1], r = s[depth/2]; // 中間邊/節點
// 刪除中間邊,重建圖,對兩側分別 BFS 求直徑中點...
此題是樹分治與直徑性質的經典應用,將雙源覆蓋問題轉化為直徑分割子問題,體現了樹算法的優雅性。