1. 程式人生 > >bzoj4890[Tjoi2017]城市(樹的半徑)

bzoj4890[Tjoi2017]城市(樹的半徑)

4890: [Tjoi2017]城市

Time Limit: 30 Sec  Memory Limit: 128 MB
Submit: 149  Solved: 91
[Submit][Status][Discuss]

Description

 從加里敦大學城市規劃專業畢業的小明來到了一個地區城市規劃局工作。這個地區一共有ri座城市,《-1條高速公路,保證了任意兩運城市之間都可以通過高速公路相互可達,但是通過一條高速公路需要收取一定的交通費用。小明對這個地區深入研究後,覺得這個地區的交通費用太貴。小明想徹底改造這個地區,但是由於上司給他的資源有限,因而小明現在只能對一條高速公路進行改造,改造的方式就是去掉一條高速公路,並且重新修建一條一樣的高速公路(即交通費用一樣),使得這個地區的兩個城市之間的最大交通費用最小(即使得交通費用最大的兩座城市之間的交通費用最小),並且保證修建完之後任意兩座城市相互可達。如果你是小明,你怎麼解決這個問題?

Input

輸入資料的第一行為一個整數n,代表城市個數。

接下來的n - 1行分別代表了最初的n-1條公路情況。每一行都有三個整數u,v,d。u,v代表這條公路的兩端城市標號,d代表這條公路的交通費用。

Output

 輸出資料僅有一行,一個整數,表示進行了最優的改造之後,該地區兩城市 之間最大交通費用。

Sample Input

5
1 2 1
2 3 2
3 4 3
4 5 4

Sample Output

 7

HINT

對於30%的資料,1<=n<500

對於100%的資料,1<=n<=5000

1 <= u,v <= n,1<= d <= 2000

 

/*
要想刪一條邊再添一條邊使一棵樹中兩點間最大距離最小
容易想到跟樹的直徑有關
N比較小,可以列舉要刪那一條邊。刪掉(u,v)之後樹就成了兩個聯通塊。
如果樹的直徑沒變,對答案沒有影響。 可以求出聯通塊1,2的直徑。 
(所以可以只列舉刪直徑上的邊,但我沒有...) 
那麼問題就轉化為在1,2聯通塊內分別找一個點使它到聯通塊內最遠的點距離最近。
這個距離就是樹的半徑。然後把這兩個點連起來即可。
如何維護半徑?
求直徑時需要求出每個點為起點的最長鏈和次長鏈 
考慮距離它最遠的那個點,在它的子樹內還是子樹外 
子樹內:最長鏈
子樹外:dfs維護這個點子樹外的最長鏈
如何維護一個點子樹外的最長鏈?
當dfs到一個x時,對於x和他的父親y,若x不在y的最長鏈內,那麼ans[x](x到子樹外最遠距離)為max(y最長鏈+dis[x][y],ans[y]+dis[x][y]) 
否則ans[x]為max(y的次長鏈+dis[x][y],ans[y]+dis[x][y])
最後對每種情況答案取min即可。 
*/ #include<bits/stdc++.h> #define N 5007 #define inf 0x3f3f3f3f using namespace std; int n,m,ans,cnt,dis,res; int head[N<<1],mv[N],u[N],v[N],w[N]; int dp[N][2]; bool vis[N]; struct edge{ int u,v,w,nxt; }e[N<<1]; inline void add(int u,int v,int w) { e[++cnt].v=v;e[cnt].w=w;e[cnt].nxt=head[u];head[u]=cnt; } inline int read() { int x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } void getd(int u) { for(int i=head[u];i;i=e[i].nxt) { int v=e[i].v; if(vis[v]) continue; vis[v]=1;getd(v); if(dp[u][0]<dp[v][0]+e[i].w) { dp[u][1]=dp[u][0];mv[u]=v; dp[u][0]=dp[v][0]+e[i].w; } else if(dp[u][1]<dp[v][0]+e[i].w) dp[u][1]=dp[v][0]+e[i].w; }dis=max(dis,dp[u][0]+dp[u][1]); } void getr(int u,int from) { res=min(res,max(from,dp[u][0])); for(int i=head[u];i;i=e[i].nxt) { int v=e[i].v; if(!vis[v]) continue; vis[v]=0; if(mv[u]==v) getr(v,max(dp[u][1]+e[i].w,from+e[i].w)); else getr(v,max(dp[u][0]+e[i].w,from+e[i].w)); } } void clear() { memset(dp,0,sizeof dp); memset(mv,0,sizeof mv); memset(vis,0,sizeof vis); res=inf;dis=0; } int main() { n=read(); for(int i=1;i<n;i++) { u[i]=read();v[i]=read();w[i]=read(); add(u[i],v[i],w[i]);add(v[i],u[i],w[i]); } int d1,d2,r1,r2;ans=res=inf;dis=0; for(int i=1;i<n;i++) { vis[v[i]]=1;getd(u[i]); d1=dis; dis=0;getd(v[i]); d2=dis; //聯通塊1,2的直徑 vis[v[i]]=0;getr(u[i],0); r1=res; res=inf;getr(v[i],0); r2=res; //聯通塊1,2的半徑 ans=min(ans,max(max(d1,d2),r1+r2+w[i])); clear(); } printf("%d\n",ans); return 0; }