最小直径生成树
在学习最小直径生成树(Minimum Diameter Spanning Tree)前建议先阅读 树的直径 的内容。
在无向图的所有生成树中,直径最小的那一棵生成树就是最小直径生成树。
图的绝对中心¶
求解直径最小生成树,首先需要找到 图的绝对中心,图的绝对中心 可以存在于一条边上或某个结点上,该中心到所有点的最短距离的最大值最小。
根据 图的绝对中心 的定义可以知道,到绝对中心距离最远的结点至少有两个。
令
图的绝对中心可能在某条边上,枚举每一条边
对于图中的任意一点
举例一个结点
随着图的绝对中心
对于图上的任意一结点,图的绝对中心到最远距离结点的函数就写作
并且这些折线交点中的最低点,横坐标就是图的绝对中心的位置。
图的绝对中心可能在某个结点上,用距离预选结点最远的那个结点来更新,即
算法流程¶
-
求出
\textit{rk}(i,j) -
图的绝对中心可能在某个结点上,用距离预选结点最远的那个结点来更新,遍历所有结点并用
\textit{ans}\leftarrow \min(\textit{ans},d(i,\textit{rk}(i,n)) \times 2) -
图的绝对中心可能在某条边上,枚举所有的边。对于一条边
w(u,v) u d(v,\textit{rk}(u,i)) > \max_{j=i+1}^n d(v,\textit{rk}(u,j)) \textit{ans}\leftarrow \min(\textit{ans}, d(u,\textit{rk}(u,i))+\max_{j=i+1}^n d(v,\textit{rk}(u,j))+w(u,v))
参考实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | bool cmp(int a, int b) { return val[a] < val[b]; }
void Floyd() {
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
void solve() {
Floyd();
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
rk[i][j] = j;
val[j] = d[i][j];
}
sort(rk[i] + 1, rk[i] + 1 + n, cmp);
}
int ans = INF;
// 图的绝对中心可能在结点上
for (int i = 1; i <= n; i++) ans = min(ans, d[i][rk[i][n]] * 2);
// 图的绝对中心可能在边上
for (int i = 1; i <= m; i++) {
int u = a[i].u, v = a[i].v, w = a[i].w;
for (int p = n, i = n - 1; i >= 1; i--) {
if (d[v][rk[u][i]] > d[v][rk[u][p]]) {
ans = min(ans, d[u][rk[u][i]] + d[v][rk[u][p]] + w);
p = i;
}
}
}
}
|
例题¶
最小直径生成树¶
根据图的绝对中心的定义,容易得知图的绝对中心是最小直径生成树的直径的中点。
求解最小直径生成树首先需要找到图的绝对中心。以图的绝对中心为起点,生成一个最短路径树,那么就可以得到最小直径生成树了。
参考实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | #include <bits/stdc++.h>
using namespace std;
const int MAXN = 502;
typedef long long ll;
typedef pair<int, int> pii;
ll d[MAXN][MAXN], dd[MAXN][MAXN], rk[MAXN][MAXN], val[MAXN];
const ll INF = 1e17;
int n, m;
bool cmp(int a, int b) { return val[a] < val[b]; }
void floyd() {
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
struct node {
ll u, v, w;
} a[MAXN * (MAXN - 1) / 2];
void solve() {
//求图的绝对中心
floyd();
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
rk[i][j] = j;
val[j] = d[i][j];
}
sort(rk[i] + 1, rk[i] + 1 + n, cmp);
}
ll P = 0, ansP = INF;
//在点上
for (int i = 1; i <= n; i++) {
if (d[i][rk[i][n]] * 2 < ansP) {
ansP = d[i][rk[i][n]] * 2;
P = i;
}
}
//在边上
int f1 = 0, f2 = 0;
ll disu = INT_MIN, disv = INT_MIN, ansL = INF;
for (int i = 1; i <= m; i++) {
ll u = a[i].u, v = a[i].v, w = a[i].w;
for (int p = n, i = n - 1; i >= 1; i--) {
if (d[v][rk[u][i]] > d[v][rk[u][p]]) {
if (d[u][rk[u][i]] + d[v][rk[u][p]] + w < ansL) {
ansL = d[u][rk[u][i]] + d[v][rk[u][p]] + w;
f1 = u, f2 = v;
disu = (d[u][rk[u][i]] + d[v][rk[u][p]] + w) / 2 - d[u][rk[u][i]];
disv = w - disu;
}
p = i;
}
}
}
cout << min(ansP, ansL) / 2 << '\n';
//最小路径生成树
vector<pii> pp;
for (int i = 1; i <= 501; ++i)
for (int j = 1; j <= 501; ++j) dd[i][j] = INF;
for (int i = 1; i <= 501; ++i) dd[i][i] = 0;
if (ansP <= ansL) {
for (int j = 1; j <= n; j++) {
for (int i = 1; i <= m; ++i) {
ll u = a[i].u, v = a[i].v, w = a[i].w;
if (dd[P][u] + w == d[P][v] && dd[P][u] + w < dd[P][v]) {
dd[P][v] = dd[P][u] + w;
pp.push_back({u, v});
}
u = a[i].v, v = a[i].u, w = a[i].w;
if (dd[P][u] + w == d[P][v] && dd[P][u] + w < dd[P][v]) {
dd[P][v] = dd[P][u] + w;
pp.push_back({u, v});
}
}
}
for (auto [x, y] : pp) cout << x << ' ' << y << '\n';
} else {
d[n + 1][f1] = disu;
d[f1][n + 1] = disu;
d[n + 1][f2] = disv;
d[f2][n + 1] = disv;
a[m + 1].u = n + 1, a[m + 1].v = f1, a[m + 1].w = disu;
a[m + 2].u = n + 1, a[m + 2].v = f2, a[m + 2].w = disv;
n += 1;
m += 2;
floyd();
P = n;
for (int j = 1; j <= n; j++) {
for (int i = 1; i <= m; ++i) {
ll u = a[i].u, v = a[i].v, w = a[i].w;
if (dd[P][u] + w == d[P][v] && dd[P][u] + w < dd[P][v]) {
dd[P][v] = dd[P][u] + w;
pp.push_back({u, v});
}
u = a[i].v, v = a[i].u, w = a[i].w;
if (dd[P][u] + w == d[P][v] && dd[P][u] + w < dd[P][v]) {
dd[P][v] = dd[P][u] + w;
pp.push_back({u, v});
}
}
}
cout << f1 << ' ' << f2 << '\n';
for (auto [x, y] : pp)
if (x != n && y != n) cout << x << ' ' << y << '\n';
}
}
void init() {
for (int i = 1; i <= 501; ++i)
for (int j = 1; j <= 501; ++j) d[i][j] = INF;
for (int i = 1; i <= 501; ++i) d[i][i] = 0;
}
int main() {
init();
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
ll u, v, w;
cin >> u >> v >> w;
w *= 2;
d[u][v] = w, d[v][u] = w;
a[i].u = u, a[i].v = v, a[i].w = w;
}
solve();
return 0;
}
|
例题¶
timus 1569. Networking the "Iset"
SPOJ PT07C - The GbAaY Kingdom
参考文献¶
Play with Trees Solutions The GbAaY Kingdom
build本页面最近更新:,更新历史
edit发现错误?想一起完善? 在 GitHub 上编辑此页!
people本页面贡献者:OI-wiki
copyright本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用