樹形DP-HDU2196

再來看一道樹形DP的入門題目。

例題

HDU2196-Computer
題目描述:學校有N臺電腦,這些電腦用不同長度的連成一棵樹(電腦1是根節點)。現在求離某臺電腦最遠的電腦的編號?
如圖所示
拿這張圖片舉一個例子:首先設所有電腦之間的連線都是1。那麼離1最遠的是4,距離爲3;離2最遠的是4和5,距離爲2;離3最遠的是5,距離爲3;離4最遠的是5,距離爲4;離5最遠的是4,距離爲4。
輸入
輸入的格式有點奇怪。
題目描述中已經提到電腦1是根節點。首先輸入N,然後以下N-1行每行輸入關於樹的描述。第i行有兩個數,ai和bi,意思是編號爲i+1的電腦連接着編號爲ai的電腦,並且連接所用的線長度爲bi。這就不難解釋輸入樣例了和上面的圖的聯繫了。
輸出
輸出N行,每行一個數。第i行的數代表離i電腦最遠的電腦的編號。

解析

不難看出,一棵樹中,離一個點最遠的點,有可能作爲那個點的子節點,也有可能作爲那個點的父節點或父節點的其它子節點。所以情況需要我們就要深搜兩次,第一次解決子節點方向,第二次解決i的父節點方向。
設dp[i][0]表示i點的最遠距離,dp[i][1]表示i點的次遠距離。
設id[i][0]表示一個與i相鄰的點,離i點的最遠點在它的方向上,id[i][1]表示一個與i相鄰的點,離i點的次遠點在它的方向上。
然後是兩次深搜~
第一次dfs1:從根節點深搜一次,更新每個點i的dp[i][0]和dp[i][1]。當然只限於i的子節點方向的一種情況。第二次dfs2:再從根節點深搜一次,這次就可以用i的父節點方向的情況更新dp[i][0]和dp[i][1]的值。
最後只需輸出dp[i][0]即可~

代碼

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=21000;
int dp[N][2],id[N][2],n;
int head[N],tol;
struct Edge{
    int to,val,next;
}edge[N];
void add(int u,int v,int c)//這裏又是鄰接表
{
    tol++;
    edge[tol].to=v;
    edge[tol].val=c;
    edge[tol].next=head[u]; 
    head[u]=tol; 
}
void Swap(int x)//交換函數
{
    if(dp[x][1]>=dp[x][0])//若次遠距離大於最遠距離,則交換二者
    {
        swap(dp[x][1],dp[x][0]);
        swap(id[x][1],id[x][0]);
    }
}
void dfs1(int u,int f)//第一次深搜
{
    dp[u][0]=0;  
    dp[u][1]=0;
    for(int i=head[u];i!=-1;i=edge[i].next)//枚舉父節點u的所有直接連接的子節點v
    {
        int v=edge[i].to;
        if(v==f)continue;
        dfs1(v,u);//深搜子節點v
        if(dp[v][0]+edge[i].val>dp[u][1])//用子節點v更新父節點u的dp值
        {
            dp[u][1]=dp[v][0]+edge[i].val;
            id[u][1]=v;
            Swap(u);
        }
    }
}
void dfs2(int u,int f)//第二次深搜
{
    for(int i=head[u];i!=-1;i=edge[i].next)//枚舉父節點u的所有直接連接的子節點v
    {
        int v=edge[i].to;
        if(v==f)continue;
        if(v==id[u][0])//如果最遠點在子節點v的方向
        {
            if(dp[u][1]+edge[i].val>dp[v][1])//則只能用父節點u的次遠點更新子節點v的dp值
            {
                dp[v][1]=dp[u][1]+edge[i].val;
                id[v][1]=u;
                Swap(v);
            }
        }
        else
        {
            if(dp[u][0]+edge[i].val>dp[v][1])//否則可以用父節點u的最遠點更新子節點v的dp值
            {
                dp[v][1]=dp[u][0]+edge[i].val;
                id[v][1]=u;
                Swap(v);
            }
        }
        dfs2(v,u);
    }
}
int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;++i)head[i]=-1;
        tol=0;
        for(int i=2;i<=n;++i)
        {
            int u,c;
            scanf("%d%d",&u,&c);
            add(u,i,c); 
            add(i,u,c);
        }
        dfs1(1,-1);
        dfs2(1,-1);
        for(int i=1;i<=n;++i)printf("%d\n",dp[i][0]);
    }
    return 0;
}

如果你覺得我的解釋有些費解,那麼請畫一下圖。有些事情真的不是言語能承載的!

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