给定一棵树,树中包含 nn 个结点(编号1~n)和 n−1 条无向边,每条边都有一个权值。
请你在树中找到一个点,使得该点到树中其他结点的最远距离最近。
输入格式
第一行包含整数 n。
接下来 n−1行,每行包含三个整数 ai,bi,ci表示点 ai和 bi之间存在一条权值为 ci 的边。
输出格式
输出一个整数,表示所求点到树中其他结点的最远距离。
数据范围
1≤n≤10000
1≤ai,bi≤n
1≤ci≤1e5
输入样例:
5
2 1 1
3 2 1
4 3 1
5 1 1
输出样例:
2
思路:随便拿出一个点作为根节点,先用普通的树形dp用子节点更新父节点,求出每个点的子结点中的最长链与次长链。然后用父节点更新子节点,求出每个点往上走的最长链。因为这个点可能是父节点最长链的取值点,所以还需要记录父节点的最长链取自哪个子节点。详见代码及注释
#include<bits/stdc++.h>
#define ll long long
#define inf 1e9+7
using namespace std;
const int maxn = 1e4+10;
struct dd{
int to,val;
};
vector<dd>q[maxn];
int n,a,b,c;
int d1[maxn],d2[maxn],up[maxn];
//d1用于记录每个点往下走的最长链,d2用于记录每个点往下走的次长链
//up用于记录每个点往上走的最长链
int book[maxn],p[maxn];
//book用于判断每个点是否为叶子节点
//p为每个点最长链的从属点
int dfs_down(int root,int fa)
{
d1[root]=d2[root]=-inf;//给一个无穷小的初始值(虽然这题树的权值没有负数)
int len=q[root].size();
for(int i = 0; i < len; i++)
{
dd u = q[root][i];
if(u.to==fa)continue;
//因为要用子节点更新父节点,所以先递归再更新
int d = dfs_down(u.to,root)+u.val;
//更新最长链、次长链及最长链的从属
if(d>=d1[root])d2[root]=d1[root],d1[root]=d,p[root]=u.to;
else if(d>d2[root])d2[root]=d;
}
if(d1[root]==-inf)//如果最长链没有被更新过,则这个点为叶子节点
d1[root]=d2[root]=0,//最长链与次长链都为0
book[root]=1;//标记它为叶节点
return d1[root];
}
void dfs_up(int root,int fa)
{
int len=q[root].size();
for(int i = 0; i < len; i++)
{
dd u = q[root][i];
if(u.to==fa)continue;
//由父节点引出的最长链有两种情况,一种为父节点也往上走,一种为父亲节点往下走
//若此节点是父节点最长链的从属,则父亲节点往下走的最大值只能用父节点的次长链来更新
if(u.to==p[root]) up[u.to]=max(up[root],d2[root])+u.val;
else up[u.to]=max(up[root],d1[root])+u.val;
//因为要用父节点更新子节点,所以先更新再递归
dfs_up(u.to,root);
}
}
int main(){
scanf("%d",&n);
for(int i = 1; i < n;i++)
{
scanf("%d %d %d",&a,&b,&c);
//无向边,存边
q[a].push_back({b,c});
q[b].push_back({a,c});
}
dfs_down(1,-1);//用儿子更新父亲
dfs_up(1,-1);//用父亲更新儿子
//每个节点引出的最长链,要么是往上走的最长链,要么是往下走的最长链
int ans=d1[1];//根节点只能往下走,无up
for(int i = 2; i <= n; i++)
if(!book[i])ans=min(ans,max(up[i],d1[i]));
else ans=min(ans,up[i]);//叶子节点无子节点,只能往上走,无d1
cout<<ans<<endl;
return 0;
}
来源:CSDN
作者:fulan liu
链接:https://blog.csdn.net/qq_44661312/article/details/104284044