HDU-2196 Computer (树形DP)

题意:求树上任一节点i能够到达的最远距离。

思路:针对任一节点i,都有两个方向,一个向i的子树方向延伸,一个向父节点方向延伸,比较这两个方向距离取max即为最远。

当然直接遍历会超时,我们定义

dp[i][0]:i节点向子树方向的最远距离。

dp[i][1]:i节点向子树方向的次远距离。(不经过最远距离子树的另外路径)

dp[i][2]:i节点向父节点方向的最远距离。

首先我们来求解dp[i][0],dp[i][1],这个也是最简单的,任选一个节点做根节点,因为树中没有环,所以当前父节点到每个子节点路径唯一,只需要跑一边dfs,从叶子回溯到根的时候累加距离就可以了,同时比较更新最远和次远。

接下来求解dp[i][2],也就是当前i节点的父亲节点方向最远距离,只需要把父亲节点的两个方向(子树方向和其父亲方向)取最大值加上i到父亲的距离,即为当前i节点的父亲方向最大距离。同样需要一遍dfs,要通过i的父亲节点来跟新当前节点的dp[i][2],所以这次dfs自上而下更新。

设当前节点为i,i的父节点r,r到i距离x:如果dp[r][0]==dp[i][0]+x,代表r的最远距离经过i,

此时dp[i][2]=max(dp[r][2],dp[r][1])+x,此时取出r的次远距离,次远必然不经过i,和r的父亲方向比较求最大值。

如果dp[r][0]!=dp[i][0]+x,此时代表r的最远距离不经过i,那么就很方便了:

此时dp[i][2]=max(dp[r][2],dp[r][0])+x,此时取出r的最远距离和r的父节点方向比较,取最大即可。

最后dp[i][0]与dp[i][2]求最大输即为i节点能够到达的最远距离。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<string>
#include<map>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#define ll long long
#define mst(a,k) memset(a,k,sizeof(a))
using namespace std;
struct node
{
    ll x,d;
    node(){}
    node(ll x1,ll d1){x=x1;d=d1;}
};
vector<node>v[10010];
ll n,dp[10010][10],vis[10010];
void dfs1(ll now)
{
    vis[now]=1;
    for(ll i=0;i<v[now].size();i++)
    {
        ll nex=v[now][i].x;
        if(vis[nex])continue;
        dfs1(nex);
        ll tmp=dp[nex][0]+v[now][i].d;   //用nex的距离来跟新now
        if(tmp>=dp[now][0])
        {
            dp[now][1]=dp[now][0];
            dp[now][0]=tmp;
        }
        else if(tmp>dp[now][1])
        {
            dp[now][1]=tmp;
        }
    }
}
void dfs2(ll now)
{
    ll nex;
    vis[now]=1;
    for(ll i=0;i<v[now].size();i++)
    {
        nex=v[now][i].x;
        if(vis[nex])continue;
        if(dp[nex][0]+v[now][i].d==dp[now][0])   //父亲节点最长距离经过i和不经过i两种情况
        {
            dp[nex][2]=max(dp[now][2],dp[now][1])+v[now][i].d;
        }
        else
        {
            dp[nex][2]=max(dp[now][2],dp[now][0])+v[now][i].d;
        }
        dfs2(nex);
    }
}
int main()
{
    ll x,d;
    while(~scanf("%lld",&n))
    {
        for(ll i=1;i<=n;i++)v[i].clear();
        mst(dp,0);
        for(ll i=2;i<=n;i++)
        {
            scanf("%lld%lld",&x,&d);
            v[i].push_back(node(x,d));
            v[x].push_back(node(i,d));   //记得存双向边,我们要求两个方向的,需要正反遍历这棵树
        }
        mst(vis,0);
        dfs1(1);   //求dp[i][0],dp[i][1]
        mst(vis,0);
        dfs2(1);   //求dp[i][2]
        for(ll i=1;i<=n;i++)
        {
            ll ans=max(dp[i][0],dp[i][2]);
            printf("%lld\n",ans);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/s540239976/article/details/80083604