Appearance
Kruskal
将边集升序后,使用并查集贪心合并顶点不在同一个连通块中的边即可。
时间复杂度:
cpp
sort(edge.begin(), edge.end());
for (auto [u, v, w] : edge) {
if (dsu.same(u, v))
continue;
dsu.merge(u, v);
}Prim
朴素维护
Prim 本质上求的是一棵根向叶子的有向树,用
每次扩展一个
cpp
dis[1] = 0;
for (int _ = 1; _ < n; _++) {
int mini = 0;
for (int i = 1; i <= n; i++) {
if (dis[i] < dis[mini] && !vis[i])
mini = i;
}
vis[mini] = 1;
for (auto [v, w] : p[mini]) {
if (!vis[v])
dis[v] = min(dis[v], w);
}
}堆优化
时间复杂度同 Dijkstra,使用二叉堆维护
cpp
dis[1] = 0;
q.push({1, 0});
for (int _ = 1; _ < n; _++) {
while (q.size() && vis[q.top().v])
q.pop();
if (q.empty())
break;
auto [now, __] = q.top();
vis[now] = 1;
for (auto [v, w] : p[now]) {
if (!vis[v]) {
if (dis[v] > w) {
q.push({v, w});
dis[v] = w;
}
}
}
}其他数据结构优化
容易发现,堆优化 Prim 只是用二叉堆优化了取最小值的过程。
那么同样地,完全可以使用任意其他可以维护最值的数据结构来优化 Prim,如线段树、set 等。
同时,根据其他数据结构的功能,还可以扩展出其他存图方式,本质上只要能维护 dis 数组即可。
Boruvka
每次对于每个连通块,向外找到最短的一条边连接。每个连通块都会向外连一条边,最坏情况下连通块数量也会
cpp
int cnt = n - 1;
dsu.init(n);
vector<bool> vis(n + 1);
while (cnt) {
for (int i = 1; i <= n; i++) {
vector<int> tmp;
vec[i] = tmp;
}
for (int i = 1; i <= n; i++) vec[dsu.find(i)].push_back(i);
for (int i = 1; i <= n; i++) {
if (vec[i].empty()) continue;
for (auto u : vec[i]) {
vis[u] = 1;
}
int minn = inf;
pii mini = {0, 0};
for (auto u : vec[i]) {
for (auto [v, w] : p[u]) {
if (vis[v]) continue;
if (minn > w) {
minn = w;
mini = {u, v};
}
}
}
if (minn == inf) {
// 无解
return;
}
if (!dsu.same(mini.first, mini.second)) {
dsu.merge(mini.first, mini.second);
cnt--;
}
for (auto u : vec[i]) {
vis[u] = 0;
}
}
}Boruvak 本质上只要找到当前每个连通块向外的最小边即可,如果条件允许,也不见得一定要遍历所有边。
次小生成树
次小生成树一定是最小生成树替换一条边得到的
对于不在最小生成树上的边
根据替换的边权是否严格小于
瓶颈路
可以证明,最小生成树上
反之,最大瓶颈路指
Kruskal 重构树
在 Kruskal 运行过程中,对于树边
最小树形图
最小树形图是在带权有向图上的最小叶向有向生成树。
朱刘算法
cpp
int zl(int n, int r) {
int res = 0;
while (1) {
vector<node> pre(n + 1);
for (int i = 1; i <= n; i++)
pre[i].w = inf;
for (auto u : edge) {
if (u.v == r)
continue;
if (u.w <= pre[u.v].w) {
pre[u.v] = {u.u, u.w};
}
}
for (int i = 1; i <= n; i++)
if (i != r)
res += pre[i].w;
stack<int> q;
vector<bool> vis(n + 1);
vector<int> num(n + 1), col(n + 1);
int id = 0;
for (int i = 1; i <= n; i++) {
int now = i;
if (col[now] || i == r)
continue;
while (now && !vis[now] && !col[now]) {
q.push(now);
vis[now] = 1;
now = pre[now].v;
}
if (vis[now]) {
id++;
while (q.top() != now) {
int cur = q.top();
q.pop();
num[cur] = id;
vis[cur] = 0;
col[cur] = 2;
}
q.pop();
num[now] = id;
vis[now] = 0;
col[now] = 2;
}
while (q.size()) {
int cur = q.top();
q.pop();
num[cur] = ++id;
vis[cur] = 0;
col[cur] = 1;
}
}
bool flag = 0;
for (int i = 1; i <= n; i++)
if (col[i] == 2) {
flag = 1;
break;
}
if (!flag)
break;
vector<Edge> tmp;
for (auto u : edge) {
if (num[u.u] == num[u.v])
continue;
tmp.push_back({num[u.u], num[u.v], u.w - pre[u.v].w});
}
edge = tmp;
r = num[r];
n = id;
}
return res;
}时间复杂度:
左偏树优化。
时间复杂度:
