有向圖 n 點 m 邊,點 1 為根,邊權為歐氏距離。求以根為根的最小樹形圖 (有向最小生成樹) 權值和,不存在輸出 "poor snoopy"。
w'(u,v) = w(u,v) - in[v] (v 為環內節點)。double zhuliu(int root, int n, int m, Edge edges[]) {
double res = 0;
while (true) {
// 1. 選入邊
fill(in, in+n, INF);
for (int i = 0; i < m; ++i)
if (edges[i].u != edges[i].v && edges[i].cost < in[edges[i].v]) {
pre[edges[i].v] = edges[i].u;
in[edges[i].v] = edges[i].cost;
}
// 檢查可達性
for (int i = 0; i < n; ++i)
if (i != root && in[i] == INF) return -1;
// 2. 找環並縮點
int cnt = 0;
fill(id, id+n, -1); fill(vis, vis+n, -1);
in[root] = 0;
for (int i = 0; i < n; ++i) {
res += 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 < n; ++i) if (id[i] == -1) id[i] = cnt++;
// 3. 重構邊
for (int i = 0; i < m; ) {
int v = edges[i].v;
edges[i].u = id[edges[i].u];
edges[i].v = id[edges[i].v];
if (edges[i].u != edges[i].v) edges[i++].cost -= in[v];
else swap(edges[i], edges[--m]);
}
n = cnt; root = id[root];
}
return res;
}
zhuliu(0, n, edge_cnt, edges),根節點 0 對應原 1 號點。