【HDU2196】【树形DP】

Computer

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 6272    Accepted Submission(s): 3163


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
 

Author
scnu
 

Recommend
lcy   |   We have carefully selected several similar problems for you:   1561  1011  3456  1520  2242 

Solution

题目大意:给定一颗树,对于任意一个节点,求出该节点到所有叶子节点距离的最大值。

有两种方法。一种是裸DP 一种是用树的直径巧妙的解决问题,(这个当做结论记着)
先说DP
对已一个点的状态只可能由父亲节或者儿子节点的状态转移过来。
所以先处理儿子节点转移过来的情况。
首先一遍 DFS 求出 每个节点在该节点为根的子树中距离最远的点以及距离第二远的点。
请注意,这里第二远的点指的是,由该节点的儿子节点转移过来的第二远的点。
比如说 1 的儿子有 2 3 4,最远的点指的是 到所有儿子节点最远的距离加上儿子节点到1节点的距离的最远的点。第二远的点是指所有儿子转移到根节 点的状态中距离第二远的点。即 fast[1=max[fast[2],fast[3],fast[4]] faster[1]=second_max(fast[2],fast[3],fast[4]) 最开始我理解错了。所以特别指出来。

为什么记录第二远的点。是因为如果父亲节点到叶子节点最远的距离,与儿子节点到叶子节点最远的距离如果经过了相同的路径,父亲节点转移过来的状态就是错误的。
之后再来一遍DFS 分析父亲节点和儿子节点一起转移过来的状态进行dp

这里假设 dp[i][1]指最远的点。dp[i][2]是次远的点。u指i节点的父亲,v指i节点的儿子,len1指儿子到当前的距离 len2指当前到父亲的距离
if(dp[u][1]==dp[v][1]+len1+len2  //这里指父亲和儿子都是由相同路径转移过来的
dp[i][1]=max(dp[v][1]+len1,dp[u][2]+len2)   //那么就是儿子的最远的与父亲次远点比较
else
dp[i][1]=max(dp[v][1]+len1,dp[u][1]+len2)
大概就是这样。

遗憾的是我在hdu上提交的代码丢失了。
也不是很麻烦,不过还是有80多行的样子。

那么第二种很简单的方法。
就是利用树的直径
首先用DFS求出树的直径(这个方法就不说了)
然后,以直径的两端为根节点,求出所有点到两个根节点的距离 dist1[i],dist2[i]
最终的答案就是max(dist1[i],dist2[i])

证明1:
还是要关系到如何求直径
首先找出任意一个点,求出树中到它最远的点,记为x
在找出距离x点最远的点 y
x到y之间的路径就是直径,当然,x,y,也是直径的两端。

因此:对于任意一个树上的节点u ,离u最远的点就是树直径的两端。
证明完成。

证明2:
反证法。
假设现在已经找到了树的直径,两端记为x,y
如果存在某一个叶子节点s,使u点到该叶子节点的距离大于u点到x或者到y的距离。
那么,树的直径的两端就会有一端变成s,自己画图可以看出来。
与树的直径所符合的性质相悖。
证明完成。

然后代码。。找不到了
随便在网上找一个
/*
HDU 2196
G++ 46ms  916K

*/

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
const int MAXN=10010;
struct Node
{
    int to;
    int next;
    int len;
}edge[MAXN*2];//因为存无向边,所以需要2倍
int head[MAXN];//头结点
int tol;
int maxn[MAXN];//该节点往下到叶子的最大距离
int smaxn[MAXN];//次大距离
int maxid[MAXN];//最大距离对应的序号
int smaxid[MAXN];//次大的序号

void init()
{
    tol=0;
    memset(head,-1,sizeof(head));
}

void add(int a,int b,int len)
{
    edge[tol].to=b;
    edge[tol].len=len;
    edge[tol].next=head[a];
    head[a]=tol++;
    edge[tol].to=a;
    edge[tol].len=len;
    edge[tol].next=head[b];
    head[b]=tol++;
}

//求结点v往下到叶子结点的最大距离
//p是v的父亲结点
void dfs1(int u,int p)
{
    maxn[u]=0;
    smaxn[u]=0;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==p)continue;//不能往上找父亲结点
        dfs1(v,u);
        if(smaxn[u]<maxn[v]+edge[i].len)
        {
            smaxn[u]=maxn[v]+edge[i].len;
            smaxid[u]=v;
            if(smaxn[u]>maxn[u])
            {
                swap(smaxn[u],maxn[u]);
                swap(smaxid[u],maxid[u]);
            }
        }
    }
}
//p是u的父亲结点,len是p到u的长度
void dfs2(int u,int p)
{
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==p)continue;
        if(v==maxid[u])
        {
            if(edge[i].len+smaxn[u]>smaxn[v])
            {

                smaxn[v]=edge[i].len+smaxn[u];
                smaxid[v]=u;
                if(smaxn[v]>maxn[v])
                {
                    swap(smaxn[v],maxn[v]);
                    swap(smaxid[v],maxid[v]);
                }
            }
        }
        else
        {
            if(edge[i].len+maxn[u]>smaxn[v])
            {
                smaxn[v]=edge[i].len+maxn[u];
                smaxid[v]=u;
                if(smaxn[v]>maxn[v])
                {
                    swap(smaxn[v],maxn[v]);
                    swap(maxid[v],smaxid[v]);
                }
            }
        }
        dfs2(v,u);
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;
    int v,len;
    while(scanf("%d",&n)!=EOF)
    {
        init();
        for(int i=2;i<=n;i++)
        {
            scanf("%d%d",&v,&len);
            add(i,v,len);
        }
        dfs1(1,-1);


        dfs2(1,-1);
        for(int i=1;i<=n;i++)
          printf("%d\n",maxn[i]);
    }
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值