HDU-2196Computer 树形DP-经典题

  • 题面

    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.
    >[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kK9AZxXE-1621860602661)(https://raw.githubusercontent.com/unique-pure/PicLibrary/main/img/C128-1005-1.JPG)]
    Hint: the example input is corrsponding 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
    
  • 题目大意

    给出一棵树,求离每个节点最远的点的距离

  • 解题思路

    这种问题我们很容易想到树形DP,而对于这种无根树,我们必须要知道节点 u u u的最远距离是怎么来的:

    • 来自它的孩子部。
    • 来自它的父亲部。

    如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9CPu4qnC-1621860692472)(https://raw.githubusercontent.com/unique-pure/PicLibrary/main/img/image-20210524203516576.png)]

求孩子部的最远距离好求,我们利用树形 D P DP DP递归从下到上求解即可。而来自父亲部的最远距离我们怎么求呢?那么我们就需要知道父亲部的最远距离怎么求?有两种情况。

  • 父节点的父亲部。
  • 父节点的孩子部。

由上可知,父节点的孩子部可能包含了我们这蓝色的部分,即我们要求的 u u u的孩子部,这显然是不合理的,我们需要排除这种情况。则我们就可以判断一下:我们需要判断一下,假如fu的最远点路径经过了u,那么我们就不继承他,改为继承fu子树的次远点距离。那么也就是说,我们是先求出来自孩子部的最远距离和次远距离,这需要我们利用一次树形 D P DP DP d f s dfs dfs解决,而如果需要求来自父亲部的最远距离的话,我们需要通过继承我们的父节点的最远距离和次远距离,这样才能求解。而第二遍的树形 D P DP DP方程如下:

f [ u ] [ 0 ] f[u][0] f[u][0]为节点 u u u孩子部的最远距离, f [ u ] [ 1 ] f[u][1] f[u][1]为节点 u u u孩子部的次远距离,而 f [ u ] [ 2 ] f[u][2] f[u][2]则为节点 u u u父亲部的次元距离。

u u u f u fu fu的最远点路径上,那么 f [ u ] [ 2 ] = m a x ( f [ u ] [ 1 ] , f [ u ] [ 2 ] ) + w [ u ] [ v ] f[u][2]=max(f[u][1],f[u][2])+w[u][v] f[u][2]=max(f[u][1],f[u][2])+w[u][v]

u u u不在 f u fu fu的最远点路径上,那么 f [ u ] [ 2 ] = m a x ( f [ u ] [ 0 ] , f [ u ] [ 2 ] ) + w [ u ] [ v ] f[u][2]=max(f[u][0],f[u][2])+w[u][v] f[u][2]=max(f[u][0],f[u][2])+w[u][v]

跑完两边之后易知每个节点 i i i的最远距离为 m a x ( f [ i ] [ 0 ] , f [ i ] [ 2 ] ) max(f[i][0],f[i][2]) max(f[i][0],f[i][2])

  • AC代码
/**
  *@filename:HDU-Computer
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-05-19 21:47
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 20010;
const int P = 1e9 + 7;

int n;
ll f[N][3];//其中我们可以用f[i][0]表示i节点孩子部的最远点距离,f[i][1]表示为i节点孩子部的次远点距离。f[i][1]表示为i节点父亲部的最远距离。
struct Edge{
    //next为当前边的下一条边,to为此边终点,w为边权值。
    int next,to;
    ll w;
}edges[N];
int head[N],tot;//head[i]表示以i为起点的第一条边的编号,若为-1则表示该起点没有边。tot则表示边的编号。
int son[N];//son[u]表示u的最大路径经过了son[u].
void add(int u,int v,ll w){
    //加边函数。
    tot++;
    edges[tot].to = v;
    edges[tot].w = w;
    edges[tot].next = head[u];
    head[u] = tot;//改变第一条边的编号。
}
void init(){
    //初始化。
    tot = 0;
    memset(head,-1,sizeof(head));
    memset(f,0,sizeof(f));
    memset(son,0,sizeof(son));
}
void dfs1(int u,int fu){
    //第一遍dfs。求出每个点u向下的最大距离和次大距离。并且用son[u]记录最大路径经过了与u直接相邻的哪一个子节点。
    for(int i = head[u]; i != -1; i = edges[i].next){
        int v = edges[i].to;
        if(v == fu)continue;
        int w = edges[i].w;
        dfs1(v,u);//递归子树。
        if(f[u][0] < f[v][0] + w){
            son[u] = v;//记录最大路径的子树节点。
            f[u][1] = f[u][0];//更新次大距离。
            f[u][0] = f[v][0] + w;
        }
        else if(f[u][1] < f[v][0] + w){
            f[u][1] = f[v][0] + w;
        }
    }
}
void dfs2(int u,int fu){
    //第二遍dfs,对于当前节点u,如果满足son[u] = v;说明u的最大路径经过了v,
    //v一定不能通过u的最大路来更新答案。只能通过u的次大路。而u的次大路一定的那个不会经过v。
    for(int i = head[u]; i != -1; i = edges[i].next){
        int v = edges[i].to;
        int w = edges[i].w;
        if(v == fu)continue;
        if(son[u] != v){
            f[v][2] = max(f[u][0],f[u][2]) + w;
        }
        else{
            f[v][2] = max(f[u][1],f[u][2]) + w;
        }
        dfs2(v,u);
    }
}
void solve(){
    dfs1(1,0);
    f[1][2] = f[1][1];//根节点的路径一样。
    dfs2(1,0);
    for(int i = 1; i <= n; i++){
        cout << max(f[i][2],f[i][0]) << endl;
    }
}
int main(){
    while(cin >> n){
        init();
        int y;
        ll z;
        for(int x = 2; x <= n; x++){
            cin >> y >> z;
            add(x,y,z);
            add(y,x,z);
        }
        solve();
    }
    return 0;
}
posted @   unique_pursuit  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示