ZOJ 3820 Building Fire Stations
阿新 • • 發佈:2019-04-07
empty sca auto pac esp ++ vector sta amp
題意:
一棵樹中,選取兩點,使得(其他點到這兩點的距離的最小值)的最大值最小
思路:
考慮只選取一點的情況,那麽顯然選取直徑的中點即為答案。
首先考慮選取的兩點肯定在直徑上,因為假如不在直徑上,那麽把點往直徑上移動,不會使得答案變得更劣。
再考慮選取兩點,假如我們將樹按照樹的直徑的中點分成兩段,那麽肯定是一邊放一點。
因為假如兩個點都在一邊的話,答案肯定是\(\frac{len}{2}\),\(len\)為樹的直徑長度。
那麽既然兩個點分別在兩邊的話,那顯然是兩邊分別求個樹的直徑,再取中點放是最優的吧。
#include <bits/stdc++.h> using namespace std; #define N 200010 int n; vector <int> G[N]; int fa[N], dep[N], far; int get_far(int st) { queue <int> q; q.push(st); dep[st] = 1; fa[st] = -1; while (!q.empty()) { int u = q.front(); q.pop(); far = u; for (auto v : G[u]) if (v != fa[u]) { fa[v] = u; dep[v] = dep[u] + 1; q.push(v); } } } void get_center(int st) { get_far(st); get_far(far); int shift = dep[far] / 2; if (dep[far] % 2 == 0) --shift; while (shift--) far = fa[far]; } int main() { int T; cin >> T; while (T--) { scanf("%d", &n); for (int i = 1; i <= n; ++i) G[i].clear(); for (int i = 1, u, v; i < n; ++i) { scanf("%d%d", &u, &v); G[u].push_back(v); G[v].push_back(u); } get_center(1); int center = far; for (vector <int> ::iterator it = G[center].begin(); it != G[center].end(); ++it) if (*it == fa[center]) { G[center].erase(it); break; } for (vector <int> :: iterator it = G[fa[center]].begin(); it != G[fa[center]].end(); ++it) if (*it == center) { G[fa[center]].erase(it); break; } int p1, p2, dis; get_center(fa[center]); p1 = far; dis = dep[p1]; get_center(center); p2 = far; dis = max(dis, dep[p2]) - 1; printf("%d %d %d\n", dis, p1, p2); } return 0; }
ZOJ 3820 Building Fire Stations