1. 程式人生 > >hdu3721: Building Roads(求樹的中心,直徑)

hdu3721: Building Roads(求樹的中心,直徑)


hdu3721: Building Roads

  Time Limit: 10 Sec
  Memory Limit: 128 MB

Description

   There is a magic planet in the space. There is a magical country on the planet. There are N cities in the country. The country is magical because there are exactly N-1 magic roads between the N cities, and from each city, it is possible to visit any other city. But after the huge expansion of the country, the road system seems to be messy. The moderator decided to rebuild the road system.
   As a worker, I cannot do too much things. I could just move one road to another position connecting arbitrary 2 cities using my magic, keeping its length unchanged. Of course, afterwards all the N cities have to be still connected. I wonder how to move in order to make the farthest distance between any two cities minimum. Could you solve it for me?
 

Input

   The first line of the input is one integer T (T ≤ 10), and then T test cases follow.
Each test case begins with a line contains only one integer N (N ≤ 2500), means there are N magic cities. The cities are numbered from 0 to N-1.
Following N-1 lines, each line has 3 integers a, b and c, means there is a magic road between a and b with distance c. (0 <= a, b < N, 0 < c <= 1000)
 

Output

   For each test case, output the case number and the corresponding minimum farthest distance. See sample for detailed format.
 

Sample Input

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

Sample Output

  Case 1: 4
  Case 2: 7
  

HINT

題目地址:hdu3721: Building Roads

題目大意: 題目很簡潔了:)

題解:

  如何求樹的中心
  1.從 i 出發向上,即終點不在以 i 為根的子樹內的最長路徑 up[i]
  2.從 i 出發向下,即終點在以 i 為根的子樹內的最長路徑 d1[i] 和次長路徑 d2[i](c1[i],c2[i]記錄從哪轉移過來)
  首先,一便樹形 dp 算出 d1,d2,c1,c2
     若d1[u]<d1[v]+e[i].val ——> d2[u]=d1[u],c2[u]=c1[u],
                  d1[u]=d1[v]+e[i].val,c1[u]=v
  否則,若d2[u]<d1[v]+e[i].val ——> d2[u]=d1[v]+e[i].val,c2[u]=v;

  d1,d2,c1,c2相對好求,如何求 up
  如果 fa[u] 的向下最長鏈不是從 v 過來的 ——> up[u]=max(d1[v],up[v])+e[i].val
  如果 fa[u] 的向下最長鏈是從 v 過來的  ——> up[u]=max(d2[v],up[v])+e[i].val

  畫圖看看


AC程式碼

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=2505,inf=25e5+5;
int Q,n;
int U[N],V[N],W[N];
bool vis[N];
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct edge{
    int to,val,next;
}e[N+N];
int cnt_edge,last[N];
bool flag[N+N];
inline void add_edge(int u,int v,int w){
    e[++cnt_edge]=(edge){v,w,last[u]};last[u]=cnt_edge;flag[cnt_edge]=1;
    e[++cnt_edge]=(edge){u,w,last[v]};last[v]=cnt_edge;flag[cnt_edge]=1;
}
int d1[N],d2[N],c1[N];
void dfs(int u,int fa){
    d1[u]=d2[u]=0;                                  //賦值前清0
    for(int i=last[u];i;i=e[i].next)
        if(flag[i]){
            int v=e[i].to;
            if(v==fa)continue;
            dfs(v,u);                               //因為是從兒子更新自己先做兒子
            if(d1[u]<d1[v]+e[i].val){               //發現從自己兒子到自己有更優解
                d2[u]=d1[u];                        //把最優解給次優
                d1[u]=d1[v]+e[i].val;c1[u]=v;       //記一下最優解從哪裡來(求 up[] 時有用)
            }else if(d2[u]<d1[v]+e[i].val)
                d2[u]=d1[v]+e[i].val;
        }
}
int up[N];
void _dfs(int u,int fa){
    vis[u]=1;
    for(int i=last[u];i;i=e[i].next)
        if(flag[i]){
            int v=e[i].to;
            if(v==fa)continue;
            if(c1[u]!=v)up[v]=max(d1[u],up[u])+e[i].val;    //看上解釋
            else up[v]=max(d2[u],up[u])+e[i].val;
            _dfs(v,u);                              //因為是自己更新兒子,先更新再 dfs
        }
}
int main(){
    Q=read();
    for(int Case=1;Case<=Q;Case++){
        n=read();
        cnt_edge=0;
        memset(last,0,sizeof(last));
        for(int i=1;i<n;i++){
            U[i]=read()+1,V[i]=read()+1,W[i]=read();//因為點的編號是 0..n-1 
            add_edge(U[i],V[i],W[i]);
        }
        int res=inf;
        for(int i=1;i<n;i++){
            flag[i*2-1]=flag[i*2]=0;                //刪邊
            dfs(U[i],0);                            //求 d1[],d2[] (c1[],c2[])
            dfs(V[i],0);
            int dep1,dep2,mx=0;
            dep1=dep2=inf;
            memset(vis,0,sizeof(vis));
            up[U[i]]=0;                             //沒有父親 清0
            _dfs(U[i],0);                           //求一顆樹的中心
            for(int j=1;j<=n;j++)
                if(vis[j] && max(d1[j],up[j])<dep1) //求出一顆樹的中心的最深dep1 (中心點編號 u1)
                    dep1=max(d1[j],up[j]);
            memset(vis,0,sizeof(vis));
            up[V[i]]=0;
            _dfs(V[i],0);
            for(int j=1;j<=n;j++)
                if(vis[j] && max(d1[j],up[j])<dep2) //求出另一顆樹的中心的最深dep1 (中心點編號 u2)
                    dep2=max(d1[j],up[j]);
            int ans=dep1+dep2+W[i];                 //算出從新連邊過的最長鏈
            for(int j=1;j<=n;j++)
                mx=max(mx,d1[j]+d2[j]);             //兩顆樹最長的直徑
            ans=max(ans,mx);
            res=min(res,ans);
            flag[i*2-1]=flag[i*2]=1;                //把邊加回來
        }
        printf("Case %d: %d\n",Case,res);
    }
    return 0;
}


  作者:skl_win
  出處:https://www.cnblogs.com/shaokele/
  本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。