学习笔记:朱刘算法
树形图:
- 无环
- 除根外每个点入度为 \(1\) (或:每个点父节点唯一)
最小树形图问题:找出总边权和最小的树形图
朱刘算法解决最小树形图问题。
算法流程(每次迭代):
-
对于除根外每个点,找出该点入边中权值最小的边,把权加到答案中。
-
判断选出的边是否存在环。若无环,退出,找到最小树形图,若有环,继续执行步骤 3。
-
将所有环缩点,构造一个新图,对于旧图的每条边:
- 若这条边在环内,删去
- 否则:若该边终点在环内,权值改为原先权值 - 该终点当前入边(在这个环上)的权: \(W - W'\)。
算法正确性:
-
对于每个环而言,至少去掉一条边。
-
对于每个环而言,必然存在一个最优解,只去一条边(若选了两条,那么把其中一条边选回环上,答案不
会变差)。
-
算法即在满足1、2性质的所有树形图中求最优解。
-
选改动过的权值,若当前边 = 该终点当前选的边,那么权值换为 \(0\),相当于标记已经选完;否则,选这条边相当于改变一个点的入边,可以映射到左边的一个树形图。
从某种角度上来说,朱刘算法就是带后悔的贪心。
算法复杂度:
\(O(nm)\),每次迭代一次点数至少会 \(-1\)。
板子
\(n\) 是点数,\(m\) 是边数,\(e\) 结构体数组是每条边 \((u, v, w)\),\(ans\) 是答案。
\(in\) 是每个点入边边权,\(pre\) 是每个点入边的起点编号,\(id\) 是缩点后的编号,\(vis\) 是找环辅助数组。
找环:
- 每次循环一个点 \(i\),从 \(v = i\) 开始一直走 \(pre\),直到 \(vis[v]\) 非空,如果走的路径构成一个环,即停下来以后 \(vis[v] = i\),这样把这个环上的点赋值 \(id\) 即可。(网上很多找环都是 \(O(n^2)\) 的。。)
double inline edmonds() {
double ans = 0;
while (true) {
for (int i = 1; i <= n; i++) in[i] = INF;
memset(vis, 0, sizeof vis);
memset(id, 0, sizeof id);
for (int i = 1; i <= m; i++)
if (e[i].w < in[e[i].v]) in[e[i].v] = e[i].w, pre[e[i].v] = e[i].u;
for (int i = 1; i <= n; i++)
if (in[i] == INF && i != rt) return -1;
col = 0;
for (int i = 1; i <= n; i++) {
if (i == rt) continue;
ans += in[i];
int v = i;
while (!vis[v] && !id[v] && v != rt)
vis[v] = i, v = pre[v];
if (v != rt && vis[v] == i) {
id[v] = ++col;
for (int x = pre[v]; x != v; x = pre[x]) id[x] = col;
}
}
if (!col) break;
for (int i = 1; i <= n; i++) if (!id[i]) id[i] = ++col;
int tot = 0;
for (int i = 1; i <= m; i++) {
int a = id[e[i].u], b = id[e[i].v];
if (a == b) continue;
e[++tot] = (E) { a, b, e[i].w - in[e[i].v] };
}
m = tot, n = col, rt = id[rt];
}
return ans;
}
模板题
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long LL;
const int N = 105, M = 10005;
const double INF = 1e100;
int n, m, rt = 1, X[N], Y[N], col;
double in[N];
int vis[N], id[N], pre[N];
struct E{
int u, v;
double w;
} e[M];
double inline edmonds() {
double ans = 0;
while (true) {
for (int i = 1; i <= n; i++) in[i] = INF;
memset(vis, 0, sizeof vis);
memset(id, 0, sizeof id);
for (int i = 1; i <= m; i++)
if (e[i].w < in[e[i].v]) in[e[i].v] = e[i].w, pre[e[i].v] = e[i].u;
for (int i = 1; i <= n; i++)
if (in[i] == INF && i != rt) return -1;
col = 0;
for (int i = 1; i <= n; i++) {
if (i == rt) continue;
ans += in[i];
int v = i;
while (!vis[v] && !id[v] && v != rt)
vis[v] = i, v = pre[v];
if (v != rt && vis[v] == i) {
id[v] = ++col;
for (int x = pre[v]; x != v; x = pre[x]) id[x] = col;
}
}
if (!col) break;
for (int i = 1; i <= n; i++) if (!id[i]) id[i] = ++col;
int tot = 0;
for (int i = 1; i <= m; i++) {
int a = id[e[i].u], b = id[e[i].v];
if (a == b) continue;
e[++tot] = (E) { a, b, e[i].w - in[e[i].v] };
}
m = tot, n = col, rt = id[rt];
}
return ans;
}
int main() {
while (~scanf("%d%d", &n, &m)) {
rt = 1;
for (int i = 1; i <= n; i++) scanf("%d%d", X + i, Y + i);
int tot = 0;
for (int i = 1; i <= m; i++) {
int a, b; scanf("%d%d", &a, &b);
e[++tot] = (E) { a, b, sqrt(((LL)X[a] - X[b]) * (X[a] - X[b]) + ((LL)Y[a] - Y[b]) * (Y[a] - Y[b])) };
}
m = tot;
double res = edmonds();
if (res == -1) puts("poor snoopy");
else printf("%.2f\n", res);
}
return 0;
}
模板题 2
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 105, M = 10005, INF = 1e9;
int n, m, rt = 1, X[N], Y[N], col, in[N];
int vis[N], id[N], pre[N];
struct E{
int u, v, w;
} e[M];
int inline edmonds() {
int ans = 0;
while (true) {
for (int i = 1; i <= n; i++) in[i] = INF;
memset(vis, 0, sizeof vis);
memset(id, 0, sizeof id);
for (int i = 1; i <= m; i++)
if (e[i].w < in[e[i].v]) in[e[i].v] = e[i].w, pre[e[i].v] = e[i].u;
for (int i = 1; i <= n; i++)
if (in[i] == INF && i != rt) return -1;
col = 0;
for (int i = 1; i <= n; i++) {
if (i == rt) continue;
ans += in[i];
int v = i;
while (!vis[v] && !id[v] && v != rt)
vis[v] = i, v = pre[v];
if (v != rt && vis[v] == i) {
id[v] = ++col;
for (int x = pre[v]; x != v; x = pre[x]) id[x] = col;
}
}
if (!col) break;
for (int i = 1; i <= n; i++) if (!id[i]) id[i] = ++col;
int tot = 0;
for (int i = 1; i <= m; i++) {
int a = id[e[i].u], b = id[e[i].v];
if (a == b) continue;
e[++tot] = (E) { a, b, e[i].w - in[e[i].v] };
}
m = tot, n = col, rt = id[rt];
}
return ans;
}
int main() {
scanf("%d%d%d", &n, &m, &rt);
int tot = 0;
for (int i = 1; i <= m; i++) {
int a, b, c; scanf("%d%d%d", &a, &b, &c);
if (b != rt && a != b) e[++tot] = (E) { a, b, c };
}
m = tot;
printf("%d\n", edmonds());
return 0;
}