首先,我們需要求出這棵樹的直徑。如何求呢?有一個很好實現,且時間複雜度僅爲 的算法。如下所述。
首先,我們隨便選一個點 ,然後找出離它最最遠的點 ,它一定是樹的一個直徑的一端。所以,我們再找出離 最遠的點 ,路徑 就是樹的一個直徑。
第二步顯然,但第一步不是很好理解。這裏簡單給大家講講。
如圖,假設 是離 最遠的點,那麼 一定是樹的一條直徑的一端。爲什麼?如果 不是直徑的一端,假設直徑的那端是 ,那麼 一定比 長,那麼 就不是離 最遠的點了。
接下來,我們考慮如何求出答案。首先,答案有兩種情況:
- 答案是某個點到直徑兩端的距離。
- 答案是某個點到非直徑端點的距離。
爲什麼呢?因爲如果答案是直徑端點到我們選出線段的距離肯定比直徑上的非直徑端點的點遠 (好拗口啊,大家多讀讀)。
我們先求出第一種情況的答案。這點比較簡單,直接用尺取法即可。畢竟在所選線段長度不超過 時,肯定是越長越好。
對於第二種情況。它只會發生在線段是一個點的情況。爲什麼?因爲如果這個線段不止 個點,那麼記 是線段上離那個非直徑上點最近的點,對於線段上其它點 ,它到那個非直徑上點的距離肯定 到那個點的距離。這種情況下,我們反而比如讓它退化。
於是,我們的思路就很清楚了。首先求出樹的直徑。然後在樹的直徑上尺取,求出候選答案 。最後求出每個非直徑點到直徑點的最短距離,記其最大值爲 。 就是答案。
爲什麼是最大值?因爲無論我們怎麼選,答案都不會小於 ,也不會小於 ,即最小答案就是 。假設讓我們得到 的點爲 ,離它最近的直徑上的點爲 ,那麼我們在線段包括 時,我們就要考慮 ,否則一定不用考慮(因爲直徑端點到它的距離一定大於 。爲什麼?這就像上面的圖一樣的情況了, 就是得到 的點, 就是直徑上的點, 就是那個 ,剩下的自己思考)。
struct edge{
int next,to,len;
}e[610];int h[310],tot;
inline void add(int a,int b,int c){
e[++tot]=(edge){h[a],b,c};h[a]=tot;
}
bool visit[310];
int dis[310],f[310];
void dfs(int u,int fa){
f[u]=fa;//記錄每個點的父親
for(int i=h[u];i;i=e[i].next){
register int to=e[i].to;
if (visit[to]) continue;
if (to==fa) continue;
dis[to]=dis[u]+e[i].len;
dfs(to,u);//遞歸求解
}
}
int n,s,ans=2e9,u,v;
inline int solve(int u){
dis[u]=0;dfs(u,0);
register int res=0;
for(int i=1;i<=n;i++)
if (dis[i]>dis[res])
res=i;
return res;
}
int main(){
scanf("%d%d",&n,&s);u=v=0;
for(int i=1,a,b,c;i<=n;i++){
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);add(b,a,c);
}
v=solve(u=solve(1));
// (u,v)就是該樹的直徑
for(int i=v,j=v;i;i=f[i]){
while (dis[j]-dis[i]>s) j=f[j];
ans=min(ans,max(dis[v]-dis[j],dis[i]));
}//在直徑上進行尺取(在長度不超限制時,越長越好)
for(int i=v;i;i=f[i])
visit[i]=true;
for(int i=v;i;i=f[i]){
dis[i]=0;dfs(i,f[i]);
}//重新搜索,不搜索直徑上點,就可以求出每個點離直徑上的點的最短路徑,大家畫個圖就可以理解了。
for(int i=1;i<=n;i++)
ans=max(ans,dis[i]);//情況2
printf("%d",ans);
return 0;
}