最小直径生成树
最小直径生成树
-
注:下文的 \(dis(u,v)\) 指的是 \(u,v\) 的最短路
-
给出一个无向图,找到其中的一颗直径最小的生成树。
-
我们考虑树有中心,是到所有节点距离最大的点最小的点。
-
类地的我们说一个图的中心,是一个点,这个点可以在图的点上,也可以是边上的一个点。
-
明确一些性质。
-
性质一:必然存在至少两个图上的点,到中心点距离最大。
-
证明:若仅仅存在一个,我们把中心点向着距离最大的那个点挪动,一定比原来更小,矛盾了。
-
那我们就先统计所有中心点在图上节点的最小直径生成树,也就是最大距离乘 \(2\) 取最小值。
-
接下来我们枚举所有边,考虑中心点在边上的情况。
-
我们枚举边 \(u,v,w\),\(c \in (0,w)\) 我们假定中心点在 \(u,v\) 边上距离 \(u\) 为 \(c\),距离 \(v\) 为 \(w-c\)
-
接下来我们其实只需要算半径就够了,也就是对于所有节点 \(t\),\(\max\limits_{i=1}^tdis(t,c)\),然后答案再对这个式子取min
-
拆以下其实 \(dis(t,c)=min(dis(t,u)+c,w-c+dis(v,t))\) 就这个式子。
-
其实就是两个一次函数取 min 变成了一条直线。
- 挂一张来自 oi-wiki 的图。
- 然后 \(\max\limits_{i=1}^tdis(t,c)\) 就相当于,多个这点放一起去 max
-
再头一张图。由于答案要对这个图像取 min,于是只要对多个最低点取 \(min\) 即可。
-
我们可以一次枚举每个最低点,我们可以先按照 \(dis(u,i)\) 给 \(i\) 排序。从达到小一次枚举 \(i\)
-
然后判断是都有交,就相当于判断上一个拐点,与当前拐点的 \(dis(v,p)<dis(v,i)\) 就有交,然后更新,具体详见代码。
-
诶,差点忘了,怎么找方案呢?对于点为中心,就直接跑最短路,然后按照网络流的方式搜索建树就好了。那要是边呢?搜索建树的同时判断最短路是否小于等于半径就好了。
后言
- 可我们要是直接取最大值和次大值更新答案,可能会有重边啊!那答案就大了
- 这这这,那我们的 性质一 就是最大手子的啊,因为它竟然只需要最大的,它它它竟不需要次大的,那我们什么都满足它,我们就分类讨论点和边,同时,中心还能在边上啊!
CodeForce 266D BerDonalds
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 220, M = N * N, inf = 0x3f3f3f3f;
int dis[N][N], rk[N][N], n, m;
int x[M], y[M], z[M];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
cin >> n >> m;
for (int i = 1;i <= n;i++)for (int j = 1;j <= n;j++)dis[i][j] = inf;
for (int i = 1, u, v, w;i <= m;i++)
cin >> x[i] >> y[i] >> z[i], dis[x[i]][y[i]] = dis[y[i]][x[i]] = z[i];
for (int i = 1;i <= n;i++)dis[i][i] = 0;
for (int k = 1;k <= n;k++)for (int i = 1;i <= n;i++)
for (int j = 1;j <= n;j++)if (dis[i][k] + dis[k][j] < dis[i][j])
dis[i][j] = dis[i][k] + dis[k][j];
for (int i = 1;i <= n;i++)
{
for (int j = 1;j <= n;j++)rk[i][j] = j;
auto cmp = [=](int x, int y) {return dis[i][x] < dis[i][y];};
sort(rk[i] + 1, rk[i] + 1 + n, cmp);
}
int ans = inf;
for (int i = 1;i <= n;i++)ans = min(ans, dis[i][rk[i][n]] << 1);
for (int id = 1;id <= m;id++)
{
int u = x[id], v = y[id], w = z[id];
for (int i = n - 1, p = n;i >= 1;i--)if (dis[v][rk[u][p]] < dis[v][rk[u][i]])
ans = min(ans, dis[u][rk[u][i]] + dis[v][rk[u][p]] + w), p = i;
}
printf("%.2lf\n", ans * 0.5);
return 0;
}

浙公网安备 33010602011771号