hdu 2196 Computer 樹形DP或者樹的直徑

Computer

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 5805    Accepted Submission(s): 2909


Problem Description
A school bought the first computer some time ago(so this computer's id is 1). During the recent years the school bought N-1 new computers. Each new computer was connected to one of settled earlier. Managers of school are anxious about slow functioning of the net and want to know the maximum distance Si for which i-th computer needs to send signal (i.e. length of cable to the most distant computer). You need to provide this information. 


Hint: the example input is corresponding to this graph. And from the graph, you can see that the computer 4 is farthest one from 1, so S1 = 3. Computer 4 and 5 are the farthest ones from 2, so S2 = 2. Computer 5 is the farthest one from 3, so S3 = 3. we also get S4 = 4, S5 = 4.
 

Input
Input file contains multiple test cases.In each case there is natural number N (N<=10000) in the first line, followed by (N-1) lines with descriptions of computers. i-th line contains two natural numbers - number of computer, to which i-th computer is connected and length of cable used for connection. Total length of cable does not exceed 10^9. Numbers in lines of input are separated by a space.
 

Output
For each case output N lines. i-th line must contain number Si for i-th computer (1<=i<=N).
 

Sample Input
5 1 1 2 1 3 1 1 1
 

Sample Output
3 2 3 4 4
 

Author
scnu

題意,給一棵樹,對於每一個結點,到離他最遠的一個點的距離是多少。

輸入是從點2開始的,i是起點,只輸入了v和w。相當於是一條i->v,邊權爲w 的邊。


有兩種解法。

第一種,利用樹的直徑,我們知道求樹的直徑的時候,是任意找一個點,dfs尋找它的最遠距離的點u,再用u相同dfs一遍找到一個v。u->v就是樹的直徑。證明:點擊打開鏈接。重點就是任意,意思就是任意一個點在樹上最遠距離的點是樹的直徑兩個端點中的一個。取max就行了。

三遍dfs,第一遍尋找直徑的一個端點

第二遍端點出發更新每一個點的距離值,順便找到另一個端點。

第三遍從另一個端點出發,再更新取大就行了。

由於這三個dfs基本上一毛一樣,所以只寫一個就行了。

CODE

#include <bits/stdc++.h>
using namespace std;
const int INF = 1e9+10;
const int N = 1e4+10;
struct node
{
    int en,val,next;
}E[N*2];
int n;
int uu;       ///樹的直徑端點
int max_dis;  ///某個點能走到的最遠距離,用來尋找樹的直徑端點
int top;      ///鄰接表邊的編號
int head[N];  ///鄰接表頭結點
int ans[N];   ///存答案

void Init()   ///初始化
{
    top = max_dis = 0;
    for(int i = 0;i < N;i++){
        head[i] = -1;
        ans[i] = 0;
    }
}

void add(int u,int v,int w){
    E[top].en = v;
    E[top].val = w;
    E[top].next = head[u];
    head[u] = top++;
}

void dfs(int u,int fa,int dis){
    if(dis >= max_dis){            ///找到距離更大的點就需要更新,max_dis不需要每次都初始化爲0
        max_dis = dis;
        uu = u;
    }
    if(ans[u] < dis) ans[u] = dis; ///每個點距離值取大
    for(int i = head[u];i != -1;i = E[i].next){
        int v = E[i].en;
        if(v == fa) continue;
        dfs(v,u,dis+E[i].val);
    }
}

int main(void)
{
    while(scanf("%d",&n) != EOF){
        Init();
        for(int i = 2;i <= n;i++){
            int v,w;
            scanf("%d %d",&v,&w);
            add(i,v,w);
            add(v,i,w);
        }
        ///需要注意的是,雖然三個都採用同一個dfs函數,但是作用卻是不一樣的
        dfs(1,-1,0);   ///第一遍尋找端點
        dfs(uu,-1,0);  ///第二遍第一個端點出發更新每個點距離,尋找另一個端點
        dfs(uu,-1,0);  ///第三遍另一個端點出發更新每個點距離
        for(int i = 1;i <= n;i++) printf("%d\n",ans[i]);
    }
    return 0;
}


第二種 :樹形DP

第一次寫樹形DP,被我瞎yy出來用樹的直徑做,2333.

樹形dp,從這道題來看,隨便定義一個根節點,每個點距離的最大值可以是從這個點的子樹來,也可以從它的父親更新而來。

存在的問題就是如果某一個點的dis最大值是由它父親更新而來,而它父親的最大距離又是由這個點更新而來。那麼這個點能被父親更新而來,那一定是父親的次長鏈加上它們連接的這條邊的距離。這就是這道樹形DP次長鏈的作用。

做法就是,第一遍任意找個點作爲根節點,dfs求出每個點在它子樹中距離的最大值的次大值。第二遍dfs從相同根節點開始更新每個點從父親結點過來是否能更新最大值。就是注意處理由父親節點更新過來的最長鏈是本身結點的問題。記錄每個點的id就行了,每個點最長鏈和次長鏈由哪一個點的更新來的,記錄它的座標。第二次dfs判斷處理就行了。

CODE

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
struct node{
    int en,val,next;
}E[N*2];
int n;
int top;        ///鄰接表邊的編號
int maxnid[N];  ///最長距離鏈的子節點編號
int smaxnid[N]; ///次長距離鏈的子節點編號
int head[N];    ///鄰接表頭結點
int maxn[N];    ///最長距離鏈長度
int smaxn[N];   ///次長距離鏈長度,secondmaxn

void Init(){    ///初始化
    top = 0;
    for(int i = 0;i <= n;i++){
        head[i] = -1;
        maxn[i] = smaxn[i] = 0;
        maxnid[i] = smaxnid[i] = i;
    }
}

void add(int u,int v,int w){
    E[top].en = v;
    E[top].val = w;
    E[top].next = head[u];
    head[u] = top++;
}

void dfs1(int u,int fa){  ///第一次更新子樹中的最長距離鏈和次長距離鏈
    for(int i = head[u];i != -1;i = E[i].next){
        int v = E[i].en;
        if(v == fa) continue;
        dfs1(v,u);
        if(maxn[v]+E[i].val > smaxn[u]){
            smaxn[u] = maxn[v]+E[i].val;
            smaxnid[u] = v;
            if(smaxn[u] > maxn[u]){
                swap(smaxn[u],maxn[u]);
                swap(smaxnid[u],maxnid[u]);
            }
        }
    }
}

void dfs2(int u,int fa){    ///第二次更新由父親結點更新來的最長鏈
    for(int i = head[u];i != -1;i = E[i].next){
        int v = E[i].en;
        if(v == fa) continue;
        if(maxnid[u] == v){ ///當前節點的父親最長鏈是它本身就用父親結點的次長鏈來更新
            if(smaxn[u]+E[i].val > smaxn[v]){
                smaxn[v] = smaxn[u]+E[i].val;
                smaxnid[v] = u;
                if(smaxn[v] > maxn[v]){
                    swap(smaxn[v],maxn[v]);
                    swap(smaxnid[v],maxnid[v]);
                }
            }
        }
        else{                ///否則就直接用父親結點的最長鏈來更新就行了
            if(maxn[u]+E[i].val > smaxn[v]){
                smaxn[v] = maxn[u]+E[i].val;
                smaxnid[v] = u;
                if(smaxn[v] > maxn[v]){
                    swap(smaxn[v],maxn[v]);
                    swap(smaxnid[v],maxnid[v]);
                }
            }
        }
        dfs2(v,u);
    }
}

int main(void)
{
    while(scanf("%d",&n) != EOF){
        Init();
        for(int i = 2;i <= n;i++){
            int v,w;
            scanf("%d%d",&v,&w);
            add(i,v,w);
            add(v,i,w);
        }
        dfs1(1,-1);
        dfs2(1,-1);
        for(int i = 1;i <= n;i++) printf("%d\n",maxn[i]);
    }
}


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