HDU - 2196 Computer 【树形dp必做题?】

传送门
//就是输出每个点的最远距离.
其实我来写首先会有树的直径, 那么就变得很简单了, 但是都说树形dp必做, 那么就用树形dp来好一点.
dp[maxn][3];
dp[u][0] 代表u的子树下距离u最远的距离是多少.
dp[u][1]代表u的子树下距离u 次 远的距离是多少.
dp[u][2] 代表经过u的父亲最远可以到达的距离.
所以转移方程方程就是:
dp[u][0] = dp[v][0] + w[i]; 所以这个需要从下往上做.
dp[v][2] = max(dp[u][2],dp[v][0] +w[i] == dp[u][0]? dp[u][1] : dp[u][0])+w[i]; 这个需要从上往下做.
// 里面那个 代表 如果 父亲可以到达的最远距离就是走的当前儿子所在的这颗子树, 那么就选父亲可以到达的次远距离, 否则就选最远距离赛.
所以两次dfs后, ans = max(dp[i][0],dp[i][2]);

AC Code

const int maxn = 1e4+5;
int cas=1;
int cnt,head[maxn];
struct node
{
    int to,w,next;
}e[maxn*2];

void add(int u,int v,int w)
{
    e[cnt] = (node){v,w,head[u]};
    head[u] = cnt++;
}
int dp[maxn][3];
void dfs_dowm_up(int u,int fa)
{
    for(int i=head[u] ; ~i ; i = e[i].next){
        int to = e[i].to;
        if(fa == to) continue;
        dfs_dowm_up(to,u);
        int w = dp[to][0] + e[i].w;
        if(w>dp[u][0]){
            dp[u][1] = dp[u][0];
            dp[u][0] = w;
        }
        else if(w > dp[u][1]){
            dp[u][1] = w;
        }
    }
}

void dfs_up_down(int u,int fa)
{
    for(int i=head[u] ; ~i ; i = e[i].next){
        int to = e[i].to;
        if(fa == to) continue;
        dp[to][2] = max(dp[u][2],dp[to][0]+e[i].w == dp[u][0]?dp[u][1]:dp[u][0]) + e[i].w;
        dfs_up_down(to,u);
    }
}
void solve()
{
    int n;
    while(~scanf("%d",&n)){
        cnt = 0 ; Fill(head,-1);
        Fill(dp,0);
        for(int i=2;i<=n;i++){
            int v,w;
            scanf("%d%d",&v,&w);
            add(i,v,w); add(v,i,w);
        }
        dfs_dowm_up(1,-1);
        dfs_up_down(1,-1);
        for(int i=1;i<=n;i++){
            printf("%d\n",max(dp[i][0],dp[i][2]));
        }
    }
}

你可能感兴趣的:(树形dp)