hdoj2196(树形dp,树的直径)

题目链接:https://vjudge.net/problem/HDU-2196

题意:给出一棵树,求每个结点可以到达的最远距离。

思路:

  如果求得是树上最长距离,两次bfs就行。但这里求的是所有点的最远距离,树形dp的经典题,想了一个小时,还是dp做得太少。分析可得对任意结点u,它的最长距离要么是向下延伸的最长距离,要不向上延伸的最长距离。

  我们用dp[u][0]表示节点u向下(子结点)的最长距离,pt[u]记录该最长距离经过的第一个子结点编号,比如最长距离经过u->v,那么pt[u]=v。dp[u][1]记录u向下的次短距离,dp[u][0]、dp[u][1]通过一次dfs可以得到,该dfs是由子结点得到父结点的信息。

  dp[u][2]记录节点u向上(父结点)的最长距离,假设u的父结点为k,分两种情况考虑:

    1.pt[k]=u(k向下的最长路经过u):dp[u][2]=wku+max(dp[k][1],dp[k][2]),即父结点走次长距离,还是向上距离。

    2.pt[k]!=u:dp[u][2]=wku+max(dp[k][0],dp[k][2]),即父结点走最长距离,还是向上距离。

  对每个结点,结果为max(dp[u][0],dp[u][2])。

AC代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

const int maxn=10005;
struct node{
    int v,w,nex;
}edge[maxn];
int n,dp[maxn][3],pt[maxn],head[maxn],cnt;

void adde(int u,int v,int w){
    edge[++cnt].v=v;
    edge[cnt].w=w;
    edge[cnt].nex=head[u];
    head[u]=cnt;
}

void dfs1(int u){
    dp[u][0]=dp[u][1]=pt[u]=0;
    for(int i=head[u];i;i=edge[i].nex){
        int v=edge[i].v,w=edge[i].w;
        dfs1(v);
        if(dp[v][0]+w>=dp[u][0]){
            pt[u]=v;
            dp[u][1]=dp[u][0];
            dp[u][0]=dp[v][0]+w;
        }
        else if(dp[v][0]+w>dp[u][1])
            dp[u][1]=dp[v][0]+w;
    }
}

void dfs2(int u){
    for(int i=head[u];i;i=edge[i].nex){
        int v=edge[i].v,w=edge[i].w;
        if(pt[u]==v) dp[v][2]=w+max(dp[u][1],dp[u][2]);
        else dp[v][2]=w+max(dp[u][0],dp[u][2]);
        dfs2(v);
    }
}

int main(){
    while(~scanf("%d",&n)){
        cnt=0;
        memset(head,0,sizeof(head));
        for(int i=2;i<=n;++i){
            int u,w;
            scanf("%d%d",&u,&w);
            adde(u,i,w);
        }
        dfs1(1);
        dfs2(1);
        for(int i=1;i<=n;++i)
            printf("%d
",max(dp[i][0],dp[i][2]));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/FrankChen831X/p/11375572.html