题目描述:
一颗有N个节点(N<2,500)的带权树。现在割去一条边,加到其他节点上,并保证也是一棵树。问最小的直径是多少?
算法分析:
枚举每条边。树形DP求出割掉这条边后,每个节点可以到的最远距离。
树形DP是万能的!!!!
#include<iostream>
#include<cstdio>
using namespace std;
// build graph
const int V = 2500 + 5;
const int E = V*2;
int head[V], nxt[E], pnt[E], cost[E], e, n;
void add(int u,int v,int c){
pnt[e] = v; cost[e] = c;
nxt[e] = head[u]; head[u] = e; e++;
}
// tree dp
const int inf = ~0u>>2;
int mx[V], sd[V];
void dfs(int u,int f){
mx[u]= 0;
sd[u] = 0;
for(int i = head[u]; i!=-1; i=nxt[i]) {
int v = pnt[i];
if(v == f) continue;
dfs(v,u);
if(cost[i] + mx[v] > mx[u]) {
sd[u] = mx[u];
mx[u] = cost[i] + mx[v];
}
else if(cost[i] + mx[v] > sd[u])
sd[u] = cost[i] + mx[v];
}
}
int temp, flag;
void dfs1(int u,int f) {
// cout<<"u: "<<u<<" "<<mx[u]<<" "<<sd[u]<<endl;
if(mx[u] < temp) temp = mx[u];
if(mx[u] > flag) flag = mx[u];
for(int i = head[u]; i!=-1; i=nxt[i]) {
int v = pnt[i];
if(v == f) continue;
// cout<<"v: "<<v<<" "<<mx[v]<<endl;
if(mx[v] + cost[i] < mx[u])
mx[v] = mx[u] + cost[i];
else if(cost[i] + sd[u] > mx[v]){
mx[v] = cost[i] + sd[u];
}
else if(cost[i] + sd[u] > sd[v]){
sd[v] = cost[i] + sd[u];
}
dfs1(v,u);
}
}
// main
int main(){
int test;
scanf("%d", &test);
for(int _ = 1; _ <= test; _ ++){
scanf("%d",&n);
int u,v,c;
//init
for(int i=0;i<n;i++) head[i] = -1;
e = 0;
// input
for(int i=1;i<n;i++) {
scanf("%d%d%d",&u,&v,&c);
add(u,v,c);
add(v,u,c);
}
int ans = inf;
for(int i=0;i<e;i+=2){
int u = pnt[i], v = pnt[i^1], mn = cost[i], mx1, mx2;
temp = inf;
flag = 0;
dfs(u,v); dfs1(u,v);
mn += temp;
mx1 = flag;
// cout<<temp<<endl;
temp = inf;
flag = 0;
dfs(v,u); dfs1(v,u);
mn += temp;
mx2 = flag;
mn = max(max(mx1,mx2),mn);
// cout<<temp<<endl;
if(mn < ans) ans = mn;
}
printf("Case %d: %d\n",_,ans);
}
}
posted on 2012-07-29 14:57
西月弦 阅读(230)
评论(3) 编辑 收藏 引用