无根树:无环无向连通图
直径:最长的路径
偏心距:直径上一段长度小于等于s的路径F,距路径F最远的结点到路径F的距离
找出最小偏心距
思路:
先找出直径以及直径上所有的点。
然后枚举所有可能的路径F,求出偏心距,然后更新最小偏心距即可
求直径的话可以通过两次dfs来求
找到直径后,枚举直径上的任意两点,找到所有长度小于等于s的路径f,求出f的偏心距
怎么求偏心距呢?遍历路径上的所有点,求出不在路径上的点距离这个点最远的距离,这些最远距离中最大的就是偏心距。
枚举时间复杂度$O(N^2)$,遍历$O(N)$,总时间复杂度为$O(N^3)$
树上问题还是不太熟,过一阵子再重新做下这个题。
#include<bits/stdc++.h>
using namespace std;
using LL = long long;
using PII = pair<int, int>;
const int N = 310, INF = 0x3f3f3f3f;
int n, s;
bool vis[N];
int ed, cnt; // cnt: 直径中点的个数 ed: 从节点u搜索,距离节点u最远的终点
int pre[N], dep[N]; // pre[u]: 记录u的父节点 dep[u]: 记录u到根节点的距离
int pres[N], dia[N];
// pres: 直径中前i个点的路径总和 dia: 存直径里的所有点
vector<pair<int, int>> adj[N];
void dfs(int u, int fa) // 遍历以u为根的子树 求出子树各个节点到根节点u的距离
{
pre[u] = fa;
for (auto [v, w] : adj[u])
{
if (v == fa || vis[v]) // 如果v在选取的路径F上则不往节点v搜索
continue;
dep[v] = dep[u] + w;
if (dep[v] > dep[ed])
ed = v;
dfs(v, u);
}
}
void get_dia() // 获取树的直径
{
dfs(1, 0);
dep[ed] = 0;
dfs(ed, 0);
// 找到直径上的所有点,存入dia数组
for (int u = ed; u; u = pre[u])
dia[++cnt] = u;
reverse(dia + 1, dia + cnt + 1);
for (int i = 1; i <= cnt; i++)
pres[i] = dep[dia[i]];
}
int solve()
{
int ans = INF;
for (int i = 1; i <= cnt; i++) // 枚举直径上的起点i和j
for (int j = i; j <= cnt; j++)
{
if (pres[j] - pres[i] <= s) //j到i的距离<=s
{
memset(vis, 0, sizeof vis);
for (int k = i; k <= j; k++)
vis[dia[k]] = 1;
int ecc = 0; // 直径上的点到直径外的点的最短距离
for (int k = i; k <= j; k++) {
dep[dia[k]] = 0, ed = 0;
dfs(dia[k], 0);
ecc = max(ecc, dep[ed]);
}
ans = min(ans, ecc);
}
}
return ans;
}
int main()
{
cin.tie(nullptr)->sync_with_stdio(false);
cin >> n >> s;
for (int i = 1; i < n; i++)
{
int u, v, w;
cin >> u >> v >> w;
adj[u].emplace_back(v, w);
adj[v].emplace_back(u, w);
}
get_dia();
cout << solve() << '\n';
return 0;
}