C++---树形DP---树的中心(每日一道算法2023.7.19)

该文介绍了如何利用树形动态规划解决寻找树中一个点,使其到其他所有点的最远距离最近的问题。通过邻接表存储图,进行两次深度优先搜索,一次向下找到最长和次长路径,一次向上更新路径长度。最终通过比较所有点的最长路径和向上路径的组合得到最小的最远距离。
摘要由CSDN通过智能技术生成

注意事项:
本题为"树形DP—树的最长路径"的近似题,同时涉及到 单链表模拟邻接表存储图 的操作,建议先理解那篇文章。

题目:
给定一棵树,树中包含 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
#include <cmath>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;

const int N = 10010, M = N*2, INF = 0x3f3f3f3f;   //无向边,M开点数的两倍
int n;
int h[N], e[M], ne[M], w[M], idx = 0;       //邻接表链表模拟
int d1[N], d2[N], p1[N], up[N];       //d1[i]为点i向下的最长路径的长度,d2[i]为次长路径的长度,p1[i]为点i的最长路径的下一个点是谁,u[i]为点i向上的最长路径的长度

//经典的邻接表-链表模拟 存储图/树
void add(int a, int b, int c) {
    e[idx] = b;
    w[idx] = c;
    ne[idx] = h[a];
    h[a] = idx++;
}

//向下找最长路径和次长路径,用子节点信息更新父节点,并返回最长路径的长度
int dfs_down(int u, int father) {
    //遍历所有点,找到从u点向下走的最长路径和次长路径
    for (int i = h[u]; i != -1; i = ne[i]) {
        int j = e[i];
        if (j == father) continue;               //向下找,所以不能向父节点找路

        int d = dfs_down(j, u) + w[i];    //求出从u点向j点走,再向其他点走的最长路径的长度

        //判断当前的这条路径是否能替换最长路径或者次长路径,并更新p1数组进行记录最长路径的下一个点
        if (d >= d1[u]) {
            d2[u] = d1[u], d1[u] = d;
            p1[u] = j;
        }
        else if (d > d2[u]) {
            d2[u] = d;
        }
    }
    return d1[u];
}

//还是向下遍历树,但这次是用当前点u来更新点j的状态,也就是父节点信息更新子节点
void dfs_up(int u, int father) {
    for (int i = h[u]; i != -1; i = ne[i]) {
        int j = e[i];
        if (j == father) continue;

        //如果点u的最长路径的下一个点(p1[u])是点j,那么就不能选最长路径来更新
        if (p1[u] == j) up[j] = max(up[u], d2[u]) + w[i];
        else up[j] = max(up[u], d1[u]) + w[i];

        dfs_up(j, u);
    }
}

int main() {
    //读入
    cin >> n;
    memset(h, -1, sizeof h);   //所有链表初始都指向-1
    for (int i = 0; i<n-1; i++) {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c), add(b, a, c);    //无向边,那就a->b,b->a双向边即可
    }

    dfs_down(1, -1);
    dfs_up(1, -1);

    //枚举所有点的向上和向下的情况,求出最短距离
    int res = INF;
    for (int i = 1; i<=n; i++) {
        res = min(res, max(d1[i], up[i]));
    }
    cout << res;
}

思路:
这个题和"树的最长路径"很相似,“树的最长路径”是只要求出 点向下的最长和次长路径的长度相加即可,
而这道题多了一种向上找路径的可能,那么不妨举个例子,还是和上题一样,将整个树“拎起来”,类似拓扑结构:
请添加图片描述
接下来以p1代表点一,p2代表点2
假设现在遍历到了p2,那如何求出p2到所有点的最长路径的长度?

条件1. 从当前节点往下,直到子树中某个叶节点的最长路径。
将p2能到的每条路(除了父节点p1)都dfs一遍即可。

条件2. 从当前节点往上走到其父节点,再从其父节点出发且不回到该节点的最长路径。
还是从上往下遍历,但这次是用p1来更新p2,首先判断p1的向下最长路径是否经过p2,
1.如果未经过p2,那么就可以直接更新p2的向上最长路径,也就是p1到p2的距离+p1的最长向下路径的长度,
2.如果经过p2,那么就说明p1的向下最长路径经过p2,也就不能使用最长路径了,这也就是为什么需要求出次长路径的原因,接着更新p2的向上最长路径,也就是p1到p2的距离+p1的次长向下路径的长度。

对所有的点进行上述两个条件的计算,再取max即为这个点到所有点的最长距离,
再将每个点的这个结果取min,即为树中某节点到其他所有节点最远距离最近的答案。

如果有所帮助请给个免费的赞吧~有人看才是支撑我写下去的动力!

声明:
算法思路来源为y总,详细请见https://www.acwing.com/
本文仅用作学习记录和交流

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值