描述
設T=(V, E, W) 是一個無圈且連通的無向圖(也稱為無根樹),每條邊到有正整數的權,我們稱T為樹網(treebetwork),其中V,E分別表示結點與邊的集合,W表示各邊長度的集合,並設T有n個結點。
路徑:樹網中任何兩結點a,b都存在唯一的一條簡單路徑,用d(a, b)表示以a, b為端點的路徑的長度,它是該路徑上各邊長度之和。我們稱d(a, b)為a, b兩結點間的距離。
D(v, P)=min{d(v, u), u為路徑P上的結點}。
樹網的直徑:樹網中最長的路徑成為樹網的直徑。對於給定的樹網T,直徑不一定是唯一的,但可以證明:各直徑的中點(不一定恰好是某個結點,可能在某條邊的內部)是唯一的,我們稱該點為樹網的中心。
偏心距ECC(F):樹網T中距路徑F最遠的結點到路徑F的距離,即
ECC(F)=max{d(v, F),v∈V}
任務:對於給定的樹網T=(V, E, W)和非負整數s,求一個路徑F,他是某直徑上的一段路徑(該路徑兩端均為樹網中的結點),其長度不超過s(可以等於s),使偏心距ECC(F)最小。我們稱這個路徑為樹網T=(V, E, W)的核(Core)。必要時,F可以退化為某個結點。一般來說,在上述定義下,核不一定只有一個,但最小偏心距是唯一的。
下面的圖給出了樹網的一個執行個體。圖中,A-B與A-C是兩條直徑,長度均為20。點W是樹網的中心,EF邊的長度為5。如果指定s=11,則樹網的核為路徑DEFG(也可以取為路徑DEF),偏心距為8。如果指定s=0(或s=1、s=2),則樹網的核為結點F,偏心距為12。
格式 輸入格式
包含n行:
第1行,兩個正整數n和s,中間用一個空格隔開。其中n為樹網結點的個數,s為樹網的核的長度的上界。設結點編號以此為1,2,……,n。
從第2行到第n行,每行給出3個用空格隔開的正整數,依次表示每一條邊的兩個端點編號和長度。例如,“2 4 7”表示串連結點2與4的邊的長度為7。
所給的資料都是正確的,不必檢驗。 輸出格式
只有一個非負整數,為指定意義下的最小偏心距。 範例 範例輸入
5 21 2 52 3 22 4 42 5 3
範例輸出
5
限制
1s 提示
40%的資料滿足:5<=n<=15
70%的資料滿足:5<=n<=80
100%的資料滿足:5<=n<=300, 0<=s<=1000。邊長度為不超過1000
這個題比較麻煩吧 寫了一天才過。。。
一開始想的是 找出直徑終點向兩邊擴充。。。
甚至都想到了二分答案驗證。。。。。。。。。
好習慣 。。。
其實呢 它只能選直徑上的一段
是什麼意思。就是必須是一段 在直徑上 可以理解成一條直徑上的一段
多條直徑 可以只處理一條
因為S只能在一條直徑上 不能分叉。。
然後兩邊dfs/bfs找直徑都會。。。
在直徑上的點找出非直徑點到它的最遠距離
枚舉樹上不超過S的一段 看看到這一段上最遠距離的點即為偏心距
都找一遍 取最小值就好了。。。
也可以用floyd處理樹上每兩個點之間的距離 不過是N^3的複雜度 加強版無法通過
此題有個加強版
見BZOJ 1999
http://www.lydsy.com:808/JudgeOnline/problem.php?id=1999
#include<iostream>#include<cstdio>#include<cstring>#include<queue>using namespace std;const int lim=500005;const int inf=999999999;int m,n,len;struct self{int x,y,w;}s[lim*2];int first[lim*2],nxt[lim*2];int root,son;int a,b,c,d[lim];bool flag[lim];int pre[lim];bool inside[lim],p;int ans=inf;int dfs(int i,int dis){ d[i]=dis; flag[i]=1; int ret=i; for(int e=first[i];e!=-1;e=nxt[e]) if(!flag[s[e].y]) { pre[s[e].y]=e; int u=dfs(s[e].y,dis+s[e].w); if(d[ret]<d[u])ret=u; } return ret;}int findlength(int i,int dis){ d[i]=dis; flag[i]=1; int ret=i; for(int e=first[i];e!=-1;e=nxt[e]) if(!flag[s[e].y]&&!inside[s[e].y]) { pre[s[e].y]=e; int u=dfs(s[e].y,dis+s[e].w); if(d[ret]<d[u])ret=u; } d[i]=d[ret]; return ret;}void isinside(int i){ inside[i]=1; if(pre[i]!=0)isinside(s[pre[i]].x);}int intree(int from,int to){ int ret=0; for(int e=pre[from];e!=0;e=pre[s[e].x]) { if(s[e].y==to)return ret; ret+=s[e].w; } return ret;}int work(int i,int len){ int ans=d[i]; int a,b,e; for(int e=pre[i];;e=pre[s[e].x]) { if(len<s[e].w||e==0) { ans=max(ans,intree(son,i)); ans=max(ans,intree(s[e].y,root)); if(e==0)p=1; return ans; } len-=s[e].w; ans=max(ans,d[s[e].x]); }} int main(){ memset(first,-1,sizeof(first)); memset(nxt,-1,sizeof(nxt)); scanf("%d%d",&m,&len); for(a=1;a<m;a++) { scanf("%d%d%d",&s[a].x,&s[a].y,&s[a].w); nxt[a]=first[s[a].x];first[s[a].x]=a; s[a+m].x=s[a].y;s[a+m].y=s[a].x;s[a+m].w=s[a].w; nxt[a+m]=first[s[a].y];first[s[a].y]=a+m; } memset(flag,0,sizeof(flag)); memset(pre,0,sizeof(pre)); root=dfs(1,0); memset(flag,0,sizeof(flag)); memset(pre,0,sizeof(pre)); son=dfs(root,0); memset(flag,0,sizeof(flag)); isinside(son); for(a=1;a<=m;a++)if(inside[a])findlength(a,0); for(a=son;;a=s[pre[a]].x) { ans=min(ans,work(a,len)); if(a==0||p)break; } cout<<ans<<endl; return 0;}