HDU-2196(树形DP)

Problem Description
A school bought the first computer some time ago(so this computer’s id is 1). During the recent years the school bought N-1 new computers. Each new computer was connected to one of settled earlier. Managers of school are anxious about slow functioning of the net and want to know the maximum distance Si for which i-th computer needs to send signal (i.e. length of cable to the most distant computer). You need to provide this information.

在这里插入图片描述
Hint: the example input is corresponding to this graph. And from the graph, you can see that the computer 4 is farthest one from 1, so S1 = 3. Computer 4 and 5 are the farthest ones from 2, so S2 = 2. Computer 5 is the farthest one from 3, so S3 = 3. we also get S4 = 4, S5 = 4.

Input
Input file contains multiple test cases.In each case there is natural number N (N<=10000) in the first line, followed by (N-1) lines with descriptions of computers. i-th line contains two natural numbers - number of computer, to which i-th computer is connected and length of cable used for connection. Total length of cable does not exceed 10^9. Numbers in lines of input are separated by a space.

Output
For each case output N lines. i-th line must contain number Si for i-th computer (1<=i<=N).

Sample Input

5
1 1
2 1
3 1
1 1

Sample Output

3
2
3
4
4

题目大意:总共有n台电脑,他们互相连接形成一棵树,相邻两台电脑相连的边权为w,问每台电脑所连接的最远的电脑的距离是多少。

解题思路:首先我们先介绍一下树的直径,树的直径为树中最长的链,那么我们知道了这个有什么用呢?当然有用啦,树的直径的一种求法是对于随便一个点w,找出离他最远的点u,然后再找出离u最远的点v,然后u-v之间的路径就是这棵树中的直径啦。为什么可以这样做呢?我们简单证明一下叭!
如果这个点w在最长路上,那么离它最远的带你一定是直径的端点之一,我们可以用反证法来证明,如果点u不是直径的端点之一,那么说明存在一个点u是树的直径的端点,因为直径是最长的链,所以(w-u)是这条最长链的一端,所以len(w-u`)>len(w-u)或者是另一个端点到w点的距离比len(w-u)大,与u是离w的最远的点矛盾。
如果点w不在最长路上,那么w-u这条链肯定与直径有一个交点j,而(w-u)与直径的后半段是重合的,在一棵树中,如果w点可以走到任何点去,我们设置一个点j为最长路上的点,而u是j可以到达的最远的点,那么len(w-u)=len(w-j)+len(j-u),我们就令j为w到达的最短的最长路上的点,在第一种情况中我们证明了w在直径上的情况,那么len(j-u)的情况就不用说明了,而又因为这是一棵树,所以其实w-j的最短走法只有一种,所以其实len(w-j)是确定的,所以我们就通过第一种情况的证明得到了第二种情况的正确性。

虽然说了这么多,但是我其实不会使用求树的直径来求我们使用树形DP叭[笑]。
我们先设置状态,先来离他最远的点可能来自哪里。最远的点可能是它的儿子结点,还可能是它的兄弟结点,或者它父亲的兄弟结点(伯父结点[笑]),也就是从他的父亲连接过来的结点。
我们设置两个状态,dp[u][0]表示从儿子结点到达的,dp[u][1]表示从父亲结点过来的,然后我们就树形DP叭。我把注释写在代码里面叭,大噶看代码就明白了。
代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
#define int long long
const int maxn=1e5+7;
const int inf=0x3f3f3f;
const int mod=10007;
struct edge
{
    int v,w,next;
}e[maxn];
int head[11000],cnt;
void add(int a,int b,int c)
{
    e[++cnt]=edge{b,c,head[a]};
    head[a]=cnt;
}
int dp[11000][3];//来自儿子 来自其他子树
int dis[11000];
void dfs(int u,int fa)
{
    dp[u][0]=0;
    int mi=0,mn=0,si=0,sn=0;//记录自己的距离最大 和第二大的儿子和这个距离
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].v;
        if(v==fa)continue;//如果是父亲结点,则continue掉
        dis[v]=e[i].w;
        dfs(v,u);
        dp[u][0]=max(dp[u][0],dp[v][0]+dis[v]);//更新后代到自己的最大距离
        if(dp[v][0]+dis[v]>mn){
            si=mi;sn=mn;
            mi=v;mn=dp[v][0]+dis[v];
        }
        else{
            if(dp[v][0]+dis[v]>sn){
                si=v,sn=dp[v][0]+dis[v];
            }
        }
    }
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].v;
        if(v==fa)continue;
        if(v==mi){
            dp[v][1]=sn+dis[v];//更新从兄弟结点到的最大距离,如果是最大的那个儿子,就加上第二大的距离
        }
        else{
            dp[v][1]=mn+dis[v];//如果不是到该父亲结点距离最大的结点,那么就直接加上那个距离最大的儿子结点的距离,
        }
    }
}
void getdis(int u,int fa)
{
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].v;
        if(v==fa)continue;
        dp[v][1]=max(dp[u][1]+dis[v],dp[v][1]);//这里先更新最大距离,然后就直接求它的兄弟结点到它的最大距离是多少,然后就带入它的儿子结点
        //dp[v][2]=max(dp[u][2]+dis[v],dp[v][1]);
        getdis(v,u);
    }
}
signed main() {
    int n;
    while(~scanf("%lld",&n)){
        cnt=0;
        memset(head,0,sizeof head);
		for(int i=2;i<=n;i++){
            int a,b;
            scanf("%lld%lld",&a,&b);
            add(i,a,b);add(a,i,b);//连接边
        }
        memset(dp,0,sizeof dp);
        memset(dis,0,sizeof dis); //点到它父亲的距离
        dfs(1,1);//求它的后代(虽然可能会追溯到很久远的时代)到它的最远距离 和它的兄弟结点的(后代)到它的最远距离
//        for(int i=1;i<=n;i++){
//        	cout<<dp[i][1]<<' ';
//		} 
//		cout<<endl;
//		for(int i=1;i<=n;i++){
//			cout<<dp[i][0]<<' ';
//		}
//		cout<<endl;
        getdis(1,1);//求从父亲得到的最大距离
        for(int i=1;i<=n;i++){
            printf("%lld\n",max(dp[i][0],dp[i][1]));
        }
    }
    return 0;
}

感觉写的挺详细的[雾]。
好吧,我确定我确实不怎么会描述。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值