HDU 2196 题解

题意简述

给一个树(带权值,给定方法:第 i i i行两个数 v , w v,w v,w,分别表示 i i i接到哪个点和边长),请依次输出从 i ( 1 &lt; = i &lt; = n ) i(1&lt;=i&lt;=n) i(1<=i<=n)点开始能到的最长路长度。

数据

输入:
5
1 1//这是第2行,所以表示2和1之间有一个长度为1的边
2 1//同理,这个表示3—2[边长=1]
3 1//这个表示4–3[边长=1]
1 1//这个表示5–1[边长=1]

5
1 1
2 1
3 1
1 1

输出
3
2
3
4
4

思路

每个点开始暴力 D F S DFS DFS O ( n ) O(n) O(n)的(以每个点为根,记录深度,求最大值, O ( n ) O(n) O(n)处理),然后每次都要换点,就是 O ( n 2 ) O(n^2) O(n2)了。
那么,我们想几个 O ( n ) O(n) O(n)的情况、
1. O ( 1 ) O(1) O(1)转换,使得我们在枚举 i i i点的时候不用每次都新算,而是珂以继承,就 O ( n ) O(n) O(n)了。
似乎不珂行。。。
2. O ( n ) O(n) O(n)求出所有答案,也就是把所有的点一块跑 D P DP DP。这样总共也是 O ( n ) O(n) O(n)的。
这个仿佛珂以,但如果不尝试的话,就真的没有办法了。
如果要这样搞,首先必须要有一个根,设1为根。
根据套路, d p [ i ] dp[i] dp[i]表示以 i i i为根能走到的最大长度。显而易见地 d p [ i ] = m a x { d p [ s o n ] + d i s ( s o n , i ) } dp[i]=max\{dp[son]+dis(son,i)\} dp[i]=max{dp[son]+dis(son,i)},也就是 m a x { 所 有 儿 子 的 max\{所有儿子的 max{dp 值 加 上 到 儿 子 的 距 离 } 值加上到儿子的距离\} }。这实在是很好理解,不细讲。
但此时我们会发现,这样还不够。又不是所有点都一定只从下面经过就是最长路。看下面这个图就知道了。
blog1.png
如果这个时候,我们以红色点为起点,然后还只是考虑这个点以下的答案(即绿色部分),就不正确了。我们应该还要考虑蓝色的部分,也就是从 i i i点上面经过的最长路径。设这个值为 d p [ i ] [ 1 ] dp[i][1] dp[i][1],刚刚的是 d p [ i ] [ 0 ] dp[i][0] dp[i][0]。不难发现 d p [ i ] [ 1 ] dp[i][1] dp[i][1]就等于(i到父亲的距离+父亲到一个不经过i点的叶节点最长距离)。
我们知道边界条件 d p [ 1 ] [ 1 ] = 0 dp[1][1]=0 dp[1][1]=0,因为 1 1 1是根节点。

那么,如何转移呢?

关键就在于第二部分,也就是父亲到一个不经过 i i i点的最长距离。大概想想,好像只要找一个离 i i i父亲最远的叶节点走过去就是最远了(1),或者是直接继承 i i i父亲往上走的最大值(2)(这个要是有肯定毫不犹豫就继承了,过会会讲如何实现的)。(2)情况也就是 d p [ f a t h e r ] [ 1 ] dp[father][1] dp[father][1],这个没什么难度。

但(1)情况不大全面,要分类一下。如果 i i i的父亲往下走的最长路要经过 i i i,就不能直接找最长了(不然走重了),珂怜的 i i i就只能继承 i i i父亲的第二长路了。

否则的情况 i i i就珂以继承 i i i父亲的第一长路。到此,我们就对这个 d p [ i ] [ 1 ] dp[i][1] dp[i][1]的转移差不多明白个大概了。但由于这个的思维难度实在是很大,所以看不懂也没有关系,要有耐心。

讲几个代码实现中要注意的问题:

1.由于 d p [ i ] [ 1 ] dp[i][1] dp[i][1]直接由父亲转移我不会写,所以我写的版本是由 i i i i i i的儿子作转移的方式
2.转移 d p [ i ] [ 1 ] dp[i][1] dp[i][1]的时候,设 m a x 1 , v 1 max1,v1 max1,v1分别为最大长度,此时经过的儿子。 m a x 2 , v 2 max2,v2 max2,v2分别为第二大长度,此时经过的儿子。对于 u u u点的儿子 v v v,如果 v = = v 1 v==v1 v==v1,说明 u u u点的最长路要从 v v v经过,所以此时只能选第二大,即 d p [ v ] [ 1 ] = m a x 2 + w dp[v][1]=max2+w dp[v][1]=max2+w,其中 w w w表示 u u u v v v的距离。
否则(即 v ! = v 1 v!=v1 v!=v1的情况), v v v就珂以选最长路了,此时 d p [ v ] = m a x 1 + w dp[v]=max1+w dp[v]=max1+w
还有一个是刚刚留下的 F l a g Flag Flag:如何记录 d p [ u ] [ 1 ] dp[u][1] dp[u][1]的值?
如果 d p [ u ] [ 1 ] &gt; m a x 1 dp[u][1]&gt;max1 dp[u][1]>max1,说明 d p [ u ] [ 1 ] dp[u][1] dp[u][1]是比所有往下走的路都要优的解,那么我们将 m a x 2 , v 2 max2,v2 max2,v2设置为 m a x 1 , v 1 max1,v1 max1,v1(即一次滑动操作),令 m a x 1 = d p [ u ] [ 1 ] max1=dp[u][1] max1=dp[u][1],把 v 1 v1 v1设置为 − 1 -1 1。那么我们在考虑儿子 v v v的时候,无论如何也不会 = = v 1 ==v1 ==v1,此时就一定珂以继承 v 1 v1 v1的值,也就是 d p [ u ] [ 1 ] dp[u][1] dp[u][1]
否则,如果 d p [ u ] [ 1 ] = = m a x 1 dp[u][1]==max1 dp[u][1]==max1或者 d p [ u ] [ 1 ] &gt; m a x 2 dp[u][1]&gt;max2 dp[u][1]>max2,说明 d p [ u ] [ 1 ] dp[u][1] dp[u][1]虽然不能作为最优解,但是珂以作为第二优的解。用和上面类似的方法更新 m a x 2 , v 2 max2,v2 max2,v2的值,当 v = = v 1 v==v1 v==v1的时候,就珂以来继承这个答案了。

好了,来看看代码吧:

#include<bits/stdc++.h>
#define N 101000
#define LogN 21
#define int long long
using namespace std;
class Graph//奇怪的存图
{
    private:
        int head[N];
        int EdgeCount;
    public:
        struct Edge
        {
            int To,Label,Next;
        }Ed[N<<1];
        void clear()
        {
            memset(head,-1,sizeof(head));
            memset(Ed,-1,sizeof(Ed));
            EdgeCount=0;
        }
        void AddEdge(int u,int v,int w)//directed
        {
            EdgeCount++;
            Ed[EdgeCount]=(Edge){v,w,head[u]};
            head[u]=EdgeCount;
        }

        int Start(int u)
        {
            return head[u];
        }

        int Next(int u)
        {
            return Ed[u].Next;
        }
        int To(int u)
        {
            return Ed[u].To;
        }
        int Label(int u)
        {
            return Ed[u].Label;
        }
}G;

int n;
void Add(int u,int v,int w)//加一个带权无向边
{
    G.AddEdge(u,v,w);G.AddEdge(v,u,w);
}
void Input()
{
    for(int i=2;i<=n;i++)
    {
        int v,w;
        scanf("%lld%lld",&v,&w);
        Add(i,v,w);
    }
}

int dp[N][2];
//dp[i][0/1]:如上解释
void DFS1(int u,int fa)//求出所有dp[i][0]的值
{
    dp[u][0]=0;
    for(int i=G.Start(u);~i;i=G.Next(i))
    {
        int v=G.To(i);
        if (v!=fa)
        {
            DFS1(v,u);
            dp[u][0]=max(dp[u][0],dp[v][0]+G.Label(i));
        }
    }
}
void DFS2(int u,int f)//求出所有dp[i][1]的值
{
    int max1=0,v1=-1;
    int max2=0,v2=-1;//和上面一样
    for(int i=G.Start(u);~i;i=G.Next(i))
    {
        int v=G.To(i),w=G.Label(i);
        if (v!=f)
        {
            int tmp=dp[v][0]+w;//到叶节点的距离
            if (tmp>max1)//说明这是最优解
            {
                max2=max1;v2=v1;//滑动一次
                max1=tmp;v1=v;//更新最优
            }
            else if (tmp==max1 or tmp>max2)//说明这是次优解
            {
                max2=tmp;
                v2=v;//更新次优
            }
        }
    }
    if (u!=1)//说明u不是根节点
    {
        int tmp=dp[u][1];//那么dp[u][1]就是有值的
        if (tmp>max1)
        {
            max2=max1;v2=v1;
            max1=tmp,v1=-1;
        }
        else if (tmp==max1 or tmp>max2)//这个上面讲了
        {
            max2=tmp;v2=-1;
        }
    }
    for(int i=G.Start(u);~i;i=G.Next(i))//转移
    {
        int v=G.To(i),w=G.Label(i);
        if (v!=f)
        {
            if (v==v1)
            {
                dp[v][1]=max2+w;
            }
            else
            {
                dp[v][1]=max1+w;
            }
            DFS2(v,u);
        }
    }
}


main()
{
    while(~scanf("%lld",&n))//处理输入(相比上面的思维要简单的多吧。。。)
    {
        G.clear();
        Input();
        memset(dp,0,sizeof(dp));
        DFS1(1,0);
        DFS2(1,0);
        for(int i=1;i<=n;i++) printf("%lld\n",max(dp[i][0],dp[i][1]));
    }
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值