树的重心【树形DP】

本文介绍了如何使用动态规划方法求解树的重心问题,即找到一个节点,使其到树中其他节点的最远距离最近。通过分析节点的下层最远距离(down1)和上层最远距离(up),并给出具体的数据范围和输入输出样例,阐述了解题思路和代码实现。

Date:2022.03.29
题意描述:
给定一棵树,树中包含 n 个结点(编号1~n)和 n−1 条无向边,每条边都有一个权值。
请你在树中找到一个点,使得该点到树中其他结点的最远距离最近。
输入格式
第一行包含整数 n。
接下来 n−1 行,每行包含三个整数 ai,bi,ci,表示点 ai 和 bi 之间存在一条权值为 ci 的边。
输出格式
输出一个整数,表示所求点到树中其他结点的最远距离。
数据范围
1≤n≤10000,
1≤ai,bi≤n,
1≤ci≤105
输入样例:
5
2 1 1
3 2 1
4 3 1
5 1 1
输出样例:
2

思路:对于一个结点uuu,与其距离最远的点可能有两种情况:
①在其下方:显然是向下走距离uuu的最远距离,记为down1[u]down1[u]down1[u]
②在其上方:记为up[u],up[u],up[u]仍然分为两种情况。
(1)从它的父节点fa[u]fa[u]fa[u]再往上走:
此时up[u]=up[fa[u]]+g[u][fa[u]];up[u]=up[fa[u]]+g[u][fa[u]];up[u]=up[fa[u]]+g[u][fa[u]];
(2)从它的父节点fa[u]fa[u]fa[u]再往下走:由于是从uuu来的,因此不能走回uuu,此时仍然有两种情况:
a.fa[u]fa[u]fa[u]向下走时,down1[fa[u]]==down1[u]+g[fa[u]][u];down1[fa[u]]==down1[u]+g[fa[u]][u];down1[fa[u]]==down1[u]+g[fa[u]][u];【即fa[u]fa[u]fa[u]向下走的最长路径经过uuu】,由于不能再经过uuu,还要保证最长,因此选择次长路径down2[fa[u]]down2[fa[u]]down2[fa[u]]
即此时:up[u]=max(up[u],down2[fa[u]]+w[i]);up[u]=max(up[u],down2[fa[u]]+w[i]);up[u]=max(up[u],down2[fa[u]]+w[i]);
b.否则选择最大路径down1[fa[u]]down1[fa[u]]down1[fa[u]]
即此时:up[u]=max(up[u],down1[fa[u]]+w[i]]);up[u]=max(up[u],down1[fa[u]]+w[i]]);up[u]=max(up[u],down1[fa[u]]+w[i]]);
除此之外,先处理出所有down1[u]、down2[u]down1[u]、down2[u]down1[u]down2[u],因为求up[u]up[u]up[u]要用到downdowndown
答案:min(max(up[i],down1[i]);min(max(up[i],down1[i]);min(max(up[i],down1[i]);

代码如下:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e4+10,M=2e4+10;
typedef long long LL;
LL n,m,f[N],d1[N],d2[N],up[N];
LL h[N],ne[M],e[M],w[M],idx;
void add(LL x,LL y,LL z)
{
    e[idx]=y;ne[idx]=h[x];w[idx]=z;h[x]=idx++;
}
LL dfs_down(LL u,LL fa)
{
    for(int i=h[u];i!=-1;i=ne[i])
    {
        LL j=e[i];
        if(j==fa) continue;
        LL d=dfs_down(j,u)+w[i];
        if(d>=d1[u]) {d2[u]=d1[u];d1[u]=d;}
        else if(d>d2[u]) d2[u]=d;
    }
    return d1[u];
}
void dfs_up(LL u,LL fa)
{
    for(int i=h[u];i!=-1;i=ne[i])
    {
        LL j=e[i];
        if(j==fa) continue;
        if(d1[u]==d1[j]+w[i]) 
            up[j]=max(up[j],max(up[u],d2[u])+w[i]);
        else if(d1[u]!=d1[j]+w[i])
            up[j]=max(up[j],max(up[u],d1[u])+w[i]);
        dfs_up(j,u);
    }
}
int main()
{
    cin>>n;memset(h,-1,sizeof h);
    for(int i=1;i<n;i++)
    {
        LL a,b,c;cin>>a>>b>>c;
        add(a,b,c);add(b,a,c);
    }
    dfs_down(1,-1);
    dfs_up(1,-1);
    LL minn=1e18;
    for(int i=1;i<=n;i++)
        minn=min(minn,max(up[i],d1[i]));
    cout<<minn;
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值