HDU-2196(樹形DP)

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

題目大意:總共有n臺電腦,他們互相連接形成一棵樹,相鄰兩臺電腦相連的邊權爲w,問每臺電腦所連接的最遠的電腦的距離是多少。

解題思路:首先我們先介紹一下樹的直徑,樹的直徑爲樹中最長的鏈,那麼我們知道了這個有什麼用呢?當然有用啦,樹的直徑的一種求法是對於隨便一個點w,找出離他最遠的點u,然後再找出離u最遠的點v,然後u-v之間的路徑就是這棵樹中的直徑啦。爲什麼可以這樣做呢?我們簡單證明一下叭!
如果這個點w在最長路上,那麼離它最遠的帶你一定是直徑的端點之一,我們可以用反證法來證明,如果點u不是直徑的端點之一,那麼說明存在一個點u是樹的直徑的端點,因爲直徑是最長的鏈,所以(w-u)是這條最長鏈的一端,所以len(w-u`)>len(w-u)或者是另一個端點到w點的距離比len(w-u)大,與u是離w的最遠的點矛盾。
如果點w不在最長路上,那麼w-u這條鏈肯定與直徑有一個交點j,而(w-u)與直徑的後半段是重合的,在一棵樹中,如果w點可以走到任何點去,我們設置一個點j爲最長路上的點,而u是j可以到達的最遠的點,那麼len(w-u)=len(w-j)+len(j-u),我們就令j爲w到達的最短的最長路上的點,在第一種情況中我們證明了w在直徑上的情況,那麼len(j-u)的情況就不用說明了,而又因爲這是一棵樹,所以其實w-j的最短走法只有一種,所以其實len(w-j)是確定的,所以我們就通過第一種情況的證明得到了第二種情況的正確性。

雖然說了這麼多,但是我其實不會使用求樹的直徑來求我們使用樹形DP叭[笑]。
我們先設置狀態,先來離他最遠的點可能來自哪裏。最遠的點可能是它的兒子結點,還可能是它的兄弟結點,或者它父親的兄弟結點(伯父結點[笑]),也就是從他的父親連接過來的結點。
我們設置兩個狀態,dp[u][0]表示從兒子結點到達的,dp[u][1]表示從父親結點過來的,然後我們就樹形DP叭。我把註釋寫在代碼裏面叭,大噶看代碼就明白了。
代碼:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
#define int long long
const int maxn=1e5+7;
const int inf=0x3f3f3f;
const int mod=10007;
struct edge
{
    int v,w,next;
}e[maxn];
int head[11000],cnt;
void add(int a,int b,int c)
{
    e[++cnt]=edge{b,c,head[a]};
    head[a]=cnt;
}
int dp[11000][3];//來自兒子 來自其他子樹
int dis[11000];
void dfs(int u,int fa)
{
    dp[u][0]=0;
    int mi=0,mn=0,si=0,sn=0;//記錄自己的距離最大 和第二大的兒子和這個距離
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].v;
        if(v==fa)continue;//如果是父親結點,則continue掉
        dis[v]=e[i].w;
        dfs(v,u);
        dp[u][0]=max(dp[u][0],dp[v][0]+dis[v]);//更新後代到自己的最大距離
        if(dp[v][0]+dis[v]>mn){
            si=mi;sn=mn;
            mi=v;mn=dp[v][0]+dis[v];
        }
        else{
            if(dp[v][0]+dis[v]>sn){
                si=v,sn=dp[v][0]+dis[v];
            }
        }
    }
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].v;
        if(v==fa)continue;
        if(v==mi){
            dp[v][1]=sn+dis[v];//更新從兄弟結點到的最大距離,如果是最大的那個兒子,就加上第二大的距離
        }
        else{
            dp[v][1]=mn+dis[v];//如果不是到該父親結點距離最大的結點,那麼就直接加上那個距離最大的兒子結點的距離,
        }
    }
}
void getdis(int u,int fa)
{
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].v;
        if(v==fa)continue;
        dp[v][1]=max(dp[u][1]+dis[v],dp[v][1]);//這裏先更新最大距離,然後就直接求它的兄弟結點到它的最大距離是多少,然後就帶入它的兒子結點
        //dp[v][2]=max(dp[u][2]+dis[v],dp[v][1]);
        getdis(v,u);
    }
}
signed main() {
    int n;
    while(~scanf("%lld",&n)){
        cnt=0;
        memset(head,0,sizeof head);
		for(int i=2;i<=n;i++){
            int a,b;
            scanf("%lld%lld",&a,&b);
            add(i,a,b);add(a,i,b);//連接邊
        }
        memset(dp,0,sizeof dp);
        memset(dis,0,sizeof dis); //點到它父親的距離
        dfs(1,1);//求它的後代(雖然可能會追溯到很久遠的時代)到它的最遠距離 和它的兄弟結點的(後代)到它的最遠距離
//        for(int i=1;i<=n;i++){
//        	cout<<dp[i][1]<<' ';
//		} 
//		cout<<endl;
//		for(int i=1;i<=n;i++){
//			cout<<dp[i][0]<<' ';
//		}
//		cout<<endl;
        getdis(1,1);//求從父親得到的最大距離
        for(int i=1;i<=n;i++){
            printf("%lld\n",max(dp[i][0],dp[i][1]));
        }
    }
    return 0;
}

感覺寫的挺詳細的[霧]。
好吧,我確定我確實不怎麼會描述。

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