树形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;
}

如果你觉得我的解释有些费解,那么请画一下图。有些事情真的不是言语能承载的!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值