【校队排位赛#7 J】 HDU 2196 Computer 树形DP 详解

43 篇文章 0 订阅
41 篇文章 0 订阅

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

给你一颗树结构,求任意结点到其他结点的路径最大值

思路:(树形动态规划)

1.首先,我们先画出一个树结构
在这里插入图片描述
2.运用我们所学过的知识,建构好一颗树之后(注意这里不是完全二叉树,可能有多分支),可以轻松用DFS求得某个结点到他的分支的最大值
如图中2为根到叶结点9的路径会比2->4的大得多(假设边权值为1)。
我们现在确实求得了2在这棵树上能到的最大距离,但是别忘了,12这个结点也可以到9上,这样使得12的最远距离也是12->9

3.通过以上例子,发现一个点的最远距离不外乎两种情况:
1.最远距离是到它叶子节点距离。如2->9 、 1->9。
2.最远距离是它“往回走”到别的结点的最远距离。这里可以将问题分解成父节点方向的最远距离 + 该节点与父节点的距离。

那么,这就有了比较和选择,便有了状态转移。
首先,我们记录一下每个节点到叶结点的最远路的路径。用 idx[root] = to,表示当前root结点要去最远路,走的下一个结点是to。因为到时我选择“往回走”的路的时候,可以是往回走一步,走去次最远路(举个例子,如果2->5的边长是100其他边都是1,那5这个结点的最远路是5->2->3->4,100+1+1)。所以我们前面DFS的时候同时记录一条次最长路。

还有一种情况就是父节点再往父节点走,再去别的结点。像12->9这种路,那么每次比较完的时候,都要记录下这种情况。这个下面细讲。

好,我们整理一下。
对上面列举的两种情况第一类,到子叶结点的最远路,我们记作dp[i][0],同时记录次短路dp[i][1]。
对于第二类,有三种情况。我们以dp[i][2]记录该节点往回走能到的最远路
1.如果当前结点已经是dp[i][0]上的点(也就是前面说的idx[root]==to,to是当前结点,root是父节点),那么往回走的最远路就是走回父节点,然后去次最长路dp【father】【1】,或者再往回走——等等!换句话说,就是找父节点往回走的路,这不就是 dp【father】【2】的意思吗?而当前结点遍历到的时候,dp【father】【2】也已经保存了最优解了。而如果当前结点不是最远路,那么两者的判断就是dp【father】【0】和dp【father】【2】!
那么它的状态转移方程就是

dp[des][2]=max(dp[root][2],idx[root]==des?dp[root][1]:dp[root][0])+p[root][i].val;

des表示当前结点,root是父节点,p[root][i].val是两点距离。

最后再对每个点比较dp【i】【0~2】即可

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
#include <cmath>
#include <string>
#define maxn 10000+500
using namespace std;

typedef struct tree
{
    int to;
    int val;
} T;

vector<T> p[maxn];
int path[maxn];
int dp[maxn][4], idx[maxn];
void dfs1(int root)
{
    for(int i=0; i<p[root].size(); i++)
    {
        int  des = p[root][i].to;
        dfs1(des);
        if(dp[root][0] < dp[des][0] + p[root][i].val)
        {
            dp[root][0] =  dp[des][0] + p[root][i].val;
            idx[root] = des;
        }
    }
    for(int i=0; i<p[root].size(); i++)
    {
        int  des = p[root][i].to;
        if(idx[root]==des)  continue;
        if(dp[root][1] < dp[des][0] + p[root][i].val)
        {
            dp[root][1] =  dp[des][0] + p[root][i].val;
        }
    }

}

void dfs2(int root)
{
    for(int i=0; i<p[root].size(); i++)
    {
        int des = p[root][i].to;
        dp[des][2]=max(dp[root][2],idx[root]==des?dp[root][1]:dp[root][0])+p[root][i].val;
        dfs2(des);
    }


}

int main()
{
    int n;
    while(cin>>n)
    {
        memset(dp,0,sizeof(dp));
        memset(idx,0,sizeof(idx));
        for(int i=0; i<=n; i++)
            p[i].clear();
        for(int i=2; i<=n; i++)
        {
            int x,dis;
            cin>>x>>dis;
            T tmp;      
            tmp.to = i;
            tmp.val = dis;
            p[x].push_back(tmp);
        }
        dfs1(1);
        dp[1][2] = 0;
        dfs2(1);
        for(int i=1; i<=n; i++)
            printf("%d\n",max(dp[i][0],dp[i][2]));
    }

    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值