最小树形图
1.算法分析
有向图上的最小生成树(Directed Minimum Spanning Tree)称为最小树形图。常用的算法是朱刘算法(也称 Edmonds 算法),可以在 O ( n m ) O(nm) O(nm) 时间内解决最小树形图问题。
该算法最后能够找到一个从根出发,能够走到任意一个点的一个最小权值的有向的树。
流程
- 对于每个点,选择它入度最小的那条边
- 如果没有环,算法终止;否则进行缩环并更新其他点到环的距离。
- 缩点时:对于环内部的边,删去;对于终点在环内的,边权变为w外-w环内。
2.模板
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 100 + 5;
const int mod = 1000000000 + 7;
const double eps = 1e-8;
typedef pair<int, int> PII;
int id[N], vis[N], pre[N], pos; // id记录每个点属于哪个环,vis用来判断几个点是否处于一个环内,
// pre记录每个点的前一个节点,pos记录虚节点
double dis[N], INF = 1e17; // dis记录每个点到最小生成树的距离
PII point[N];
// 定义边的结构
struct node {
int u, v;
double cost;
} edge[N * N];
int n, m;
// 朱刘算法
// root:根节点(记得下标从0开始)
// V点数0~V-1
// E边数0~E-1
double zhuliu(int root, int V, int E) {
double sum = 0; // 计算当前最小树形图的边权值和
while (true) {
// 把每个点到最小生成树的距离置为无穷
for (int i = 0; i < V; i++) {
dis[i] = INF;
}
//找最小入边
for (int i = 0; i < E; i++) {
int u = edge[i].u, v = edge[i].v;
if (u != v && dis[v] > edge[i].cost) {
dis[v] = edge[i].cost;
pre[v] = u;
if (u == root) {
pos = i;
}
}
}
//某点不存在入边,算法结束
for (int i = 0; i < V; i++) {
if (dis[i] == INF && i != root) return -1;
}
int cnt = 0; // 记录当前这张图内点的个数
memset(id, -1, sizeof id);
memset(vis, -1, sizeof vis);
dis[root] = 0; // 把根节点放入最小树形图
//找环
for (int i = 0; i < V; i++) {
int v = i;
sum += dis[i];
while (id[v] == -1 && vis[v] != i && v != root) {
vis[v] = i;
v = pre[v];
}
//找到环的时候缩点编号
if (id[v] == -1 && v != root) {
for (int u = pre[v]; u != v; u = pre[u]) {
id[u] = cnt; // 把u点放入编号为cnt的环内
}
id[v] = cnt++;
}
}
//如果没有环,则以找到最小树形图,算法结束
if (!cnt) break;
//把余下的不在环里的点编号
for (int i = 0; i < V; i++) {
if (id[i] == -1) id[i] = cnt++;
}
//更新距离
for (int i = 0; i < E; i++