AcWing 1073 樹的中心

題目描述:

給定一棵樹,樹中包含 n 個結點(編號1~n)和 n−1 條無向邊,每條邊都有一個權值。

請你在樹中找到一個點,使得該點到樹中其他結點的最遠距離最近。

輸入格式

第一行包含整數 n。

接下來 n−1 行,每行包含三個整數 ai,bi,ci,表示點 ai和 bi之間存在一條權值爲 ci的邊。

輸出格式

輸出一個整數,表示所求點到樹中其他結點的最遠距離。

數據範圍

1≤n≤10000,
1≤ai,bi≤n,
1≤ci≤10^5

輸入樣例:

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

輸出樣例:

2

分析:

對於樹中任意一個節點,離它最遠的點要麼是在它的子樹中,要麼最遠路徑要經過該節點的父節點。我們肯定不能暴力的對每個節點都做一次BFS來求最遠距離,需要更加高效的解法來求解本題。

如圖所示,對於任意一個節點u,離它最遠的點可能在它的子樹中,在上一題中我們知道可以通過DFS求出某個節點到其子樹中最深節點的距離,也就是求出某節點的高度。想到達離u最遠的節點也可能要經過其父節點v,如果v的最遠節點在其子樹中,則需要先判斷是否在u的子樹中,如果在,則要取v子樹中第二遠子樹的節點,否則,可以取第一遠的節點,在加上uv之間的距離,就得到了u到v的子樹中節點最遠的距離了。如果離v最遠的節點是在也要經過v的父節點才能到達,那麼還需要進一步考慮v的父節點。

用文字描述聽起來很繁瑣,設d1[u]表示u的子樹中離u最遠節點的距離,d2[u]表示u的子樹中離u第二遠節點的距離(這裏第一遠節點和第二遠節點要位於u的不同子樹中)。up[u]表示u向其父節點能夠延伸的最遠距離,即經過其父節點能夠到達的最遠距離。那麼最終u能到達的最遠距離就是d1[u]和up[u]的較大者。

下面考慮如何實現這個思路。一般而言,對樹做一次DFS是用某節點的孩子節點的信息取更新該節點的信息,但是本題既需要用孩子節點取更新父節點,又需要用父節點取更新孩子節點,所以需要做兩次DFS。第一次DFS求出每個節點到其子樹節點的最遠距離和第二遠距離。上一題的DFS就是實現該功能的,只需要將兩個距離都存儲下來即可。第二次DFS需要求出每個節點的up信息,即通過其父節點能夠到達的最遠距離,設v是u的父節點,如果d1[u] + w[uv] == d1[v],說明離v最遠的節點很可能在u的子樹中,於是考察v子樹中第二遠的節點;還要考察經過v的父節點能到達的最遠距離與v的子樹中最遠距離的大小來決定v能夠到達的最遠距離,再加上w[uv]就是u經過v能夠到達的最遠距離了。這裏DFS是先遍歷父節點再遍歷孩子節點的,所以遍歷孩子節點時需要用到父節點的up信息都是已經求好的,遍歷一次樹也就求出了樹中所有節點的up信息了。雖然描述起來比較複雜,具體實現時並不困難,實現細節見代碼:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10005,M = 20005;
int h[N],e[M],w[M],ne[M],idx;
int d1[N],d2[N],up[N];
void add(int a,int b,int c){
    e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
}
int dfs1(int u,int fa){
    for(int i = h[u];~i;i = ne[i]){
        int j = e[i];
        if(j == fa) continue;
        int d = dfs1(j,u) + w[i];
        if(d >= d1[u])  d2[u] = d1[u],d1[u] = d;
        else if(d > d2[u])  d2[u] = d;
    }
    return d1[u];
}
void dfs2(int u,int fa){
    for(int i = h[u];~i;i = ne[i]){
        int j = e[i];
        if(j == fa) continue;
        if(d1[u] == d1[j] + w[i])   up[j] = d2[u] + w[i];
        else    up[j] = d1[u] + w[i];
        if(up[u] + w[i] > up[j])    up[j] = up[u] + w[i];
        dfs2(j,u);
    }
}
int main(){
    int n,a,b,c;
    cin>>n;
    memset(h,-1,sizeof h);
    for(int i = 0;i < n - 1;i++){
        cin>>a>>b>>c;
        add(a,b,c);
        add(b,a,c);
    }
    dfs1(1,-1);
    dfs2(1,-1);
    int res = 1e9;
    for(int i = 1;i <= n;i++)   res = min(res,max(d1[i],up[i]));
    cout<<res<<endl;
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章