题意:给出一棵树,树的边有边权,你可以把一条边接到其他地方,但是要保证最后还是一棵树,使得这棵树的直径最小。
思路:很容易想到,我们操作的边肯定是原图中直径的边,我们先找出原图直径的边,然后依次判断操作这条边能获得的新图的直径,从而更新答案。显然要操作的边把树分成了两棵树,要想最后还是一棵树,这条边肯定是接到这两棵树的点上,我们枚举这个端点,可以利用树形dp求出接完边后的直径的长度,对于这两棵子树我们要处理出两个dp值,假设我们对这两棵子树已经用dfs将其转化成有根树,拿一棵子树来讲,dp1[i]表示i节点与非i的孩子子树的点所能构成的最长链的长度,dp2[i][0]表示i节点与i的孩子子树的节点所能构成的最长链的长度,dp2[i][1]表示i节点与i的孩子子树的节点所能构成的次长链的长度。我们对于这两棵树的点进行枚举,假设枚举的点为u,v,那所形成的最长链的长度为:
max(dp1[u]+dp2[u][0],dp1[v]+dp2[v][0], 两棵子树的直径).然后在所有最长链中取最小值就是答案。
现在讲转移方程:如果v不是u与u的孩子字数的节点所能构成的最长链上的点,dp1[v] = max(dp1[u], dp2[v][0]),否则dp1[v] = max(dp1[u], dp2[v][1]) (v为u的孩子)
dp2的转移直接dfs就可以转移了。
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
#define N 3000
struct E{
int v, d, ne;
E(){}
E(int _v, int _d, int _ne):v(_v), d(_d), ne(_ne){}
}e[N*2];
bool vis[N];
int size, ca = 1, head[N], dis[N], ans;
int pre[N], dp1[N], dp2[N][2], id[N][2], D[N];
queue<int>Q;
vector<int>V[2];
void init() {
size = 0;
memset(head, -1, sizeof(head));
}
void add(int u, int v, int d) {
e[size] = E(v, d, head[u]);
head[u] = size++;
}
int spfa(int u) {
memset(dis, 0x3f, sizeof(dis));
memset(vis, false, sizeof(vis));
memset(pre, -1, sizeof(pre));
while (!Q.empty()) Q.pop();
dis[u] = 0, vis[u] = true;
Q.push(u);
int i, v, re = u, mx = 0;
while (!Q.empty()) {
u = Q.front(), Q.pop();
vis[u] = false;
if (mx < dis[u]) mx = dis[u], re = u;
for (i = head[u];~i;i = e[i].ne) {
v = e[i].v;
if (dis[v] > dis[u]+e[i].d) {
dis[v] = dis[u]+e[i].d;
pre[v] = u;
if (!vis[v]) {
Q.push(v);
vis[v] = true;
}
}
}
}
ans = mx;
return re;
}
void dfs2(int u, int f, int c) {
V[c].push_back(u);
int i, v, d;
dp2[u][0] = 0, id[u][0] = u;
for (i = head[u];~i;i = e[i].ne) {
v = e[i].v, d = e[i].d;
if (v == f) continue;
dfs2(v, u, c);
int ttm = dp2[v][0], ttp = dp2[v][1];
if (ttm != -1 && ttm+d > dp2[u][0]) {
dp2[u][1] = dp2[u][0], id[u][1] = id[u][0];
dp2[u][0] = ttm+d, id[u][0] = v;
}else if (ttm != -1 && ttm+d > dp2[u][1]) {
dp2[u][1] = ttm+d, id[u][1] = v;
}
}
}
void dfs1(int u, int f) {
int v, i, d;
for (i = head[u];~i;i = e[i].ne) {
v = e[i].v, d = e[i].d;
if (v == f) continue;
dp1[v] = dp1[u]+d;
if (id[u][0] == v && dp2[u][1] != -1) {
dp1[v] = max(dp1[v], dp2[u][1]+d);
}else {
dp1[v] = max(dp1[v], dp2[u][0]+d);
}
D[v] = max(dp2[v][0],dp1[v]);
dfs1(v, u);
}
}
void cal(int u, int v) {
memset(dp1, -1, sizeof(dp1));
memset(dp2, -1, sizeof(dp2));
memset(id, -1, sizeof(id));
V[0].clear(), V[1].clear();
dp1[u] = dp1[v] = 0;
dfs2(u, v, 0), dfs2(v, u, 1);
D[u] = dp2[u][0], D[v] = dp2[v][0];
dfs1(u, v), dfs1(v, u);
int i, j, d;
for (i = head[u];~i;i = e[i].ne) {
if (e[i].v == v) break;
}
d = e[i].d;
int mx = 0;
for (i = 0;i < V[0].size();i++) {
u = V[0][i];
mx = max(dp1[u]+dp2[u][0], mx);
}
for (j = 0;j < V[1].size();j++) {
u = V[1][j];
mx = max(dp1[u]+dp2[u][0], mx);
}
for (i = 0;i < V[0].size();i++) {
for (j = 0;j < V[1].size();j++) {
u = V[0][i], v = V[1][j];
int tm = max(D[u]+D[v]+d, mx);
ans = min(tm, ans);
}
}
}
int main() {
int T, i, j, u, v, d, n;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
init();
for (i = 1;i < n;i++) {
scanf("%d%d%d", &u, &v, &d);
add(u, v, d), add(v, u, d);
}
u = spfa(spfa(0));
while (pre[u] != -1) {
v = pre[u];
cal(u, v);
u = v;
}
printf("Case %d: %d\n", ca++, ans);
}
}