题目类型 最小树形图
题目意思
给出最多100个点和10000条有向边,问从1出发的最小有向生成树的权值是多少
解题方法
最小树形图 ->
Chu-Liu/Edmonds Algorithm (最下面)
参考代码 - 有疑问的地方在下方留言 看到会尽快回复的
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn = 100 + 10;
const int INF = 1<<29;
struct Edge {
int u, v;
double cost;
}E[maxn*maxn];
struct Point {
double x, y;
}point[maxn];
double sq(int u, int v) {
double x = point[u].x - point[v].x;
double y = point[u].y - point[v].y;
return sqrt(x*x + y*y);
}
struct DM {
int NV, NE;
double in[maxn];
int id[maxn], vis[maxn], pre[maxn];
void init(int nv, int ne) {
NV = nv, NE = ne;
}
double Directed_MST(int root) {
double ans = 0;
while(true) {
for( int i=0; i<NV; i++ ) in[i] = INF;
for( int i=0; i<NE; i++ ) {
int u = E[i].u;
int v = E[i].v;
if(E[i].cost < in[v] && u != v) {
in[v] = E[i].cost;
pre[v] = u;
}
}
for( int i=0; i<NV; i++ ) {
if(i == root) continue;
if(in[i] == INF) return -1;
}
int cnt = 0;
memset(id, -1, sizeof(id));
memset(vis, -1, sizeof(vis));
in[root] = 0;
for( int i=0; i<NV; i++ ) {
ans += in[i];
int v = i;
while(vis[v] != i && id[v] == -1 && v != root) {
vis[v] = i;
v = pre[v];
}
if(v != root && id[v] == -1) {
for( int u=pre[v]; u!=v; u=pre[u] ) {
id[u] = cnt;
}
id[v] = cnt++;
}
}
if(cnt == 0) break;
for( int i=0; i<NV; i++ ) {
if(id[i] == -1) id[i] = cnt++;
}
for( int i=0; i<NE; i++ ) {
int v = E[i].v;
E[i].u = id[E[i].u];
E[i].v = id[E[i].v];
if(E[i].u != E[i].v) {
E[i].cost -= in[v];
}
}
NV = cnt;
root = id[root];
}
return ans;
}
}dm;
int main() {
freopen("in", "r", stdin);
int n, m;
while(scanf("%d%d", &n, &m) != EOF) {
for( int i=0; i<n; i++ ) scanf("%lf%lf", &point[i].x, &point[i].y);
for( int i=0; i<m; i++ ) {
scanf("%d%d", &E[i].u, &E[i].v);
E[i].u--, E[i].v--;
E[i].cost = sq(E[i].u, E[i].v);
}
dm.init(n, m);
double ans = dm.Directed_MST(0);
if(ans == -1) printf("poor snoopy\n");
else printf("%.2f\n", ans);
}
return 0;
}