題意:給出一棵樹,樹的邊有邊權,你可以把一條邊接到其他地方,但是要保證最後還是一棵樹,使得這棵樹的直徑最小。
思路:很容易想到,我們操作的邊肯定是原圖中直徑的邊,我們先找出原圖直徑的邊,然後依次判斷操作這條邊能獲得的新圖的直徑,從而更新答案。顯然要操作的邊把樹分成了兩棵樹,要想最後還是一棵樹,這條邊肯定是接到這兩棵樹的點上,我們枚舉這個端點,可以利用樹形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);
}
}