Building Roads |
---|
题意: 移动一条边,求最小的树的直径。
题解: 枚举每一条边,求删除改变之后的两棵树A、B。答案无外乎三种情况:
- 直径完全在树A中
- 直径完全在树B中
- 直径贯穿了两棵树【对于这种,求出两棵树的中心,连接两树的中心,直径自然穿过】
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e3 + 10;
const int inf = 0x3f3f3f3f;
int _T, n;
int cut, resl;
int ans, root;
int dp[maxn][3];
int cnt, head[maxn];
struct edge { int v, w, next; } e[maxn << 1];
void add (int u, int v, int w) {
e[++cnt] = {v, w, head[u]}; head[u] = cnt;
e[++cnt] = {u, w, head[v]}; head[v] = cnt;
}
void dfs (int u, int fa) {
int fir = 0, sec = 0;
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].v, w = e[i].w;
if (v == fa || i == cut || i == cut + 1) continue;
dfs(v, u);
int c = dp[v][0] + w;
if (c > fir) sec = fir, fir = c;
else if (c > sec) sec = c;
}
dp[u][0] = fir;
dp[u][1] = sec;
}
void DFS (int u, int fa) {
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].v, w = e[i].w;
if (v == fa || i == cut || i == cut + 1) continue;
if (dp[u][0] == dp[v][0] + w) dp[v][2] = max(dp[u][1], dp[u][2]) + w;
else dp[v][2] = max(dp[u][0], dp[u][2]) + w;
DFS(v, u);
}
int tem = max(dp[u][0], dp[u][2]);
if (tem < ans) ans = tem, root = u;
}
void solve () {
memset(dp, 0, sizeof dp);
int r1 = e[cut + 0].v, r2 = e[cut + 1].v;
ans = inf; dfs(r1, 0); DFS(r1, 0); r1 = root;
ans = inf; dfs(r2, 0); DFS(r2, 0); r2 = root;
int d1 = max(dp[r1][0] + max(dp[r1][1], dp[r1][2]), dp[r2][0] + max(dp[r2][1], dp[r2][2]));
int d2 = max(dp[r1][0], dp[r1][2]) + max(dp[r2][0], dp[r2][2]) + e[cut].w;
// printf("r1 r2 -> %d %d -> d1 d2 -> %d %d\n", r1, r2, d1, d2);
resl = min(resl, max(d1, d2));
}
int cas;
void run () {
scanf("%d", &n);
cnt = 0, resl = inf;
memset(head, 0, sizeof head);
for (int i = 1, u, v, w; i < n; ++i) {
scanf("%d%d%d", &u, &v, &w);
add(u + 1, v + 1, w);
}
for (cut = 1; cut < 2 * n - 1; cut += 2) solve();
printf("Case %d: %d\n", ++cas, resl);
}
int main () {
scanf("%d", &_T);
while (_T--) run();
return 0;
}