HDU 2196 树形DP + 节点到达的最长距离

本文介绍了一种使用树形动态规划(Tree DP)的方法来解决求解每个节点到树中其他节点最远距离的问题。通过两次深度优先搜索(DFS),算法能够高效地计算出每个节点所能达到的最大距离。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

树形DP + 节点到达的最长距离

题意:

​ 给出一棵树,问每一个节点能到的最远的距离。

思路:

​ 题目很简单,意思也很明白,但是如果是想求出一个节点到其它点的最远的距离,只需一次dfs就能知道,此题需要n次dfs,所以还需其它更快的算法。

​ 首先思考一个节点的最远的距离怎么算出?

​ 最远的距离只有两种可能:

1.  此节点的儿子在最远的距离上
2. 最远距离经过父亲节点

第一种很容易算出,而经过父亲节点最远距离有两种,一种是经过了父亲的其它儿子,一种是经过了父亲的的父亲。所以需要保存这两种情况,第一种可以保存父亲节点的经过儿子的最远距离和次远距离,当父亲的经过儿子的最远距离不经过当前儿子,那么就可以用父亲经过儿子的最远距离,所过经过了儿子的最远距离经过了当前节点,那么就是用次远距离。(所谓的次远距离就是不经过最远儿子距离的经过其它儿子的最远距离)

所以,三者都需要算出。定义:dp[u][0/1/2] 表示到达u点的最远距离,0表示经过儿子节点的最远距离1,1表示经过其它儿子的最远距离,2表示经过父亲节点的最远距离。

dp[u][0/1] 很容易算出,而dp[u][2] 可以通过dfs递推算出。


#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = 100005;

int n;
int dp[maxn][3];
int head[maxn],pos;
int flag[maxn];

struct Node
{
    int e,v;
    int next;
}edge[maxn*2];

void init()
{
    pos = 0;
    memset(head,-1,sizeof(head));
}

void input()
{
    for(int i = 2;i <= n; i++) {
        int b,c;
        scanf("%d%d",&b,&c);
        edge[pos].e = b;
        edge[pos].v = c;
        edge[pos].next = head[i];
        head[i] = pos++;
        edge[pos].e = i;
        edge[pos].v = c;
        edge[pos].next = head[b];
        head[b] = pos++;
    }
}

void dfs1(int u,int fa)
{
    dp[u][0] = dp[u][1] = dp[u][2] = flag[u] = 0;
    for(int i = head[u];i != -1; i = edge[i].next) {
        int to = edge[i].e;
        if(to == fa) continue;
        dfs1(to,u);
        if(dp[u][0] < dp[to][0] + edge[i].v) {
            flag[u] = to;
            dp[u][1] = max(dp[u][1],dp[u][0]);
            dp[u][0] = dp[to][0] + edge[i].v;
        }
        else 
            dp[u][1] = max(dp[u][1],dp[to][0] + edge[i].v);
    }
}

void dfs2(int u,int fa)
{
    for(int i = head[u];i != -1;i = edge[i].next) {
        int to = edge[i].e;
        if(to == fa) continue;
        if(flag[u] == to)
            dp[to][2] = max(dp[u][2],dp[u][1]) + edge[i].v;
        else 
            dp[to][2] = max(dp[u][2],dp[u][0]) + edge[i].v;
        dfs2(to,u);
    }
}

int main(int argc, char const *argv[])
{
    //freopen("in.txt","r",stdin);

    while(scanf("%d",&n) != EOF) {
        init();
        input();
        dfs1(1,-1);
        dfs2(1,-1);
        for(int i = 1;i <= n; i++)
            printf("%d\n",max(dp[i][0],dp[i][2]));
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值