AcWing1073.树的中心(树形DP)题解

题目传送门
题目描述

给定一棵树,树中包含 n 个结点(编号1~n)和 n−1 条无向边,每条边都有一个权值。

请你在树中找到一个点,使得该点到树中其他结点的最远距离最近。

输入格式

第一行包含整数 n。

接下来 n−1 行,每行包含三个整数 ai,bi,ci,表示点 ai 和 bi 之间存在一条权值为 ci 的边。

输出格式

输出一个整数,表示所求点到树中其他结点的最远距离。

数据范围

1≤n≤10000
1≤ai,bi≤n
−105≤ci≤105

输入样例:

5
2 1 1
3 2 1
4 3 1
5 1 1

输出样例:

2

题解:

树形DP:dfs俩次,第一次求以u为节点,用d1和d2来记录向下走的最长路和次长路, 并用p1 p2来记录最长路和次长路来自哪个节点, 最后取以u为节点向下走的最远距离和向上走的最远距离的最小值

#include<iostream>
#include<cstring>
using namespace std;
const int N = 10010;
int h[N], e[N * 2], w[N * 2], ne[N * 2], idx;
// d1 和 d2 分别表示以 i 为起点向下走的最大值和次大值
//up表示向上走的最长路径是多少
//p1 和 p2 表示最长路和次长路分别是从哪条边走上来的
int d1[N], d2[N], up[N], p1[N], p2[N];
int ans;
void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int dfs_d(int u, int father)//向下走
{
    if(h[u] == -1) return 0;
    d1[u] = d2[u] = -1e9;
    for(int i = h[u]; i != -1; i = ne[i]){
        int j = e[i];
        if(j == father)continue;
        int d = dfs_d(j, u) + w[i];
        if(d >= d1[u]){

            d2[u] = d1[u];
            d1[u] = d;
            p2[u] = p1[u], p1[u] = j;
        }
        else if(d > d2[u]){
            d2[u] = d;
            p2[u] = j;
        }
    }
    if(d1[u] == -1e9)d1[u] = d2[u] = 0; // 判断是否为叶子节点
    return d1[u];
} 
void dfs_u(int u, int father)//向上走
{
    for(int i = h[u]; i != -1; i = ne[i]){
        int j = e[i];
        if(j == father)continue;
        if(p1[u] == j)up[j] = max(up[u], d2[u]) + w[i];  //要么是次长路, 要什么是up[u];
        else up[j] = max(up[u], d1[u]) + w[i];  //可以是最长路,要么是up[u];
        dfs_u(j, u);
    }
}
int main()
{
    int n;
    cin >> n;
    memset(h, -1, sizeof h);
    for(int i = 0; i < n - 1; i++){
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
        add(b, a, c);
    }
    ans = 1e9;
    dfs_d(1, -1);//往下走
    dfs_u(1, -1);
    int res = 1e9;
    for(int i = 1; i <= n; i++)res = min(res, max(d1[i], up[i]));
    cout << res << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值