Computer (hdu 2196)

题目链接

据说是一道树形dp的入门题。由于现在还比较菜,得不到这些系统的结论。第一次接触树形dp,写一些其中的思路。

先说说这道题。求每一个节点的最大距离,如果只是求一个节点,我们可以用dfs来o(n)地求出到每一个点的距离,找到最大。这里有一万个点,这样肯定是不行的,需要寻求一种o(n)或者o(nlogn)的方法。树形dp是前者。

dp的关键就在状态转移。状态转移的核心,在于把目标情况分类完全。这里的情况如下来思考。把这张图看成一棵树,根节点可以任取。代码处理中我们取第一个节点当做根节点。那么每一个节点到图上其他点的最大路径有两种可能,一种是在树中,从这个节点到叶子节点的方向寻找到最大值,另一种是向父亲节点的方向寻找最大值。这两种可能性就是dp的状态思考来源。

具体实现中,使用两次深度优先搜索。树的问题处理中,递归是核心思路。第一次dfs,从下往上更新,实际就是在dfs中先进行下一次dfs,再更新当前值(从上往下更新反之)。更新每一个节点的最大路和次大路的值,以及该路径的上一个节点。这个节点的保存,用于第二次dfs的判断,第二次dfs从上往下更新,假设父节点为u,子节点为v,如果u的最大路的路径上一个节点是v,那么v向u的方向走的最大路,就是u v这条边的长度加上u的次大路的长度。不断比较更新最大值即可。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define mem(a) memset(a,0,sizeof(a))
using namespace std;
const int Max=10010;
struct node
{
    int from,to,w,next;
}edge[2*Max];
int maxid[Max],smaxid[Max],mn[Max],sn[Max],tot;//存储最大路的子节点,次大路的子节点,最大路的值,次大路的值
int head[2*Max];
void init()
{
    tot=1;
    mem(mn);
    mem(sn);
    mem(head);
}
void add(int u,int v,int w)
{
    edge[tot].next=head[u];
    edge[tot].from=u;
    edge[tot].to=v;
    edge[tot].w=w;
    head[u]=tot++;
}
void dfs1(int x,int p)//p是父亲节点,为了不让值向父亲节点的方向找
{//找到往下这个方向,每个节点的最大值和次大值 从往上更新
    for(int i=head[x];i!=0;i=edge[i].next)
    {
        int y=edge[i].to;
        if(y==p) continue;
        dfs1(y,x);
        if(sn[x]<mn[y]+edge[i].w)
        {
            sn[x]=mn[y]+edge[i].w;
            smaxid[x]=y;
            if(sn[x]>mn[x])
            {
                swap(sn[x],mn[x]);
                swap(smaxid[x],maxid[x]);
            }
        }
    }
}
void dfs2(int x,int p)
{
    //从上往下更新,把上面的节点作为父节点,找到下面节点有可能的另一个方向的值
    for(int i=head[x];i!=0;i=edge[i].next)
    {
        int y=edge[i].to;
        if(y==p) continue;
        //y向x这边走,经过x的最大路,或者x的最大路是经过y的,那么就走x的次大路
        if(y==maxid[x])//x的最大路从y这里来
        {
            if(sn[y]<sn[x]+edge[i].w)//选择x的次大路来尝试更新y
            {
                sn[y]=sn[x]+edge[i].w;
                smaxid[y]=x;
                if(sn[y]>mn[y])
                {
                    swap(sn[y],mn[y]);
                    swap(smaxid[y],maxid[y]);
                }
            }
        }
        else
        {
            if(sn[y]<mn[x]+edge[i].w)//用x的最大路来更新y
            {
                sn[y]=mn[x]+edge[i].w;
                smaxid[y]=x;
                if(sn[y]>mn[y])
                {
                    swap(sn[y],mn[y]);
                    swap(smaxid[y],maxid[y]);
                }
            }
        }
        dfs2(y,x);//更新y后,以y为父节点再往下更新
    }
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        init();
        for(int i=2;i<=n;i++)
        {
            int u,w;
            scanf("%d%d",&u,&w);
            add(i,u,w);
            add(u,i,w);
        }
        dfs1(1,-1);
        dfs2(1,-1);
        for(int i=1;i<=n;i++)
            printf("%d\n",mn[i]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xuzonghao/article/details/80924293