StkOvflow

STACK OVERFLOW!

一言(ヒトコト)

这年头 expert 很难么?
——zn

AcWing. 1072 树的最长路径

题目描述

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

现在请你找到树中的一条最长路径。

换句话说,要找到一条路径,使得使得路径两端的点的距离最远。

注意:路径中可以只包含一个点。

解题思路

首先因为是树所以有这样的一个性质:树上两点之间有且仅有一条路径,路径(x,y)可以拆分成两条路径 (x,r)(r,y),其中 r 表示 LCA(x,y)

所以我们可以得到一个思路:枚举断点 r,状态 dist[r] 表示 r 的子树中,与 r 距离最远的点的距离。对于 r 的所有子节点 x1,x2,x3,......xk,有

dist[r]=max1ik{dist[xi]+w(r,xi)}

然后再定义 f[x] 为以 x 为断点的最长链,那么树的直径就是

max1xn{f[x]}

f的过程如下图所示
切格瓦拉.png
对于 x 的任意两个子节点 yiyjf[x] 可以分成下面四个部分的和
1.yiyi 的子树的最大距离
2.yix 的距离
3.xyj 的距离
4.yjyj 子树的最大距离
因此f[x]=max1j<ikdist[yi]+dist[yj]+w(x,yi)+w(x,yj)

然后就OK了

优化

如果我么顺序枚举,那么在将要枚举到 i 的时候,对于每个已经枚举过的节点 j<i 我们都可以保存到从 x 出发走向以 yj 为根的子树最大距离,这个距离也就是

max1j<i{dist[yj]+w(x,yj)}

所以我们可以一边枚举一边用dist[x]+dist[yi]+w(x,yi) 更新 f[x], 一边用dist[yi]+w(x,yi) 更新 dist[x]

代码

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 10010, M = N << 1;
int h[N], e[M], w[M], ne[M], idx = 1;
bool st[N]; int n, dist[M], res = -0x3f3f3f;

void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}

void dp(int x)
{
    st[x] = true;
    for (int i = h[x]; ~i; i = ne[i]) 
    {
        int j = e[i];
        if (st[j]) continue ;
        dp(j);
        res = max(res, dist[x] + dist[j] + w[i]);
        dist[x] = max(dist[x], dist[j] + w[i]);
    }
    res = max(res, dist[x]);
}

int main()
{
    memset(h, -1, sizeof h);
    scanf("%d", &n);

    while (n -- ) 
    {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        add(u, v, w), add(v, u, w);
    }

    dp(1);
    printf("%d\n", res);

    return 0;
}
posted @   StkOvflow  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示