题目传送门
题目描述
给定一棵树,树中包含 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;
}