Problem Description
Input
Output
Sample Input
Sample Output
Author
Source
Solution
朱劉算法
這個題目顯然是有向圖求最小生成樹,但是因爲沒有根節點,所以認爲的將所有節點編號+1,從而引入0號根節點
在用朱劉算法求最小生成樹的之前,先考慮一下引入的根節點的問題:根節點到每個節點的距離應該爲所有邊的距離之和再加上1,最後的最小費用應該是用求出來的值減去根節點到每個節點的距離
先考慮找到最小邊,其實就是用兩點間的距離不斷更新到其前驅節點(父節點)的最小距離,同時記錄前驅節點,更新到各點距離最小的點(輸出時需減去邊數)對於存在到期前驅節點的距離爲無窮大的點顯然不能由根節點遍歷到,那麼就不存在最小生成樹
然後找環,如果該節點通過其父節點能夠再次回到自己,那麼該節點就在一個環中,將該環全部標記爲一個序號,然後不停的遞歸搜索,直到沒有環爲止,就求出了最小生成樹(break掉)
最後縮點,把環裏的所有邊的起點和終點都改爲其環的編號,並修改環中點到其父節點的距離爲減去當前換種距離後的值,對於不在環中的點,按順序爲之標號即可
Code
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <map>
#include <vector>
#include <queue>
#define L 1010
#define LL long long
#define inf 0x7f7f7f7f
using namespace std;
struct edge {
int from, to, w;
} e[L * L];
int n, m, s, t, c, cnt, sum, in[L], pre[L], mr, vis[L], id[L];
inline void add(int a, int b, int v) {
e[cnt].from = a, e[cnt].to = b, e[cnt++].w = v;
}
inline int MST(int root, int N, int E) {
int res = 0;
while (true) {
for (int i = 0; i < N; ++i) in[i] = inf;
memset(pre, -1, sizeof(pre));
for (int i = 0; i < E; ++i) {
int u = e[i].from, v = e[i].to, w = e[i].w;
if (in[v] > w && u != v) {
in[v] = w, pre[v] = u;//*
if (u == root) mr = i;
}
}
for (int i = 0; i < N; ++i) {
if (i == root) continue;
if (in[i] == inf) return -1;
}
int node = 0;
memset(vis, -1, sizeof(vis));
memset(id, -1, sizeof (id));
in[root] = 0;//*
for (int i = 0; i < N; ++i) {
res += in[i];
int v = i;
while (id[v] == -1 && v != root && vis[v] != i) vis[v] = i, v = pre[v];
if (id[v] == -1 && v != root){
for (int u = pre[v]; u != v; u = pre[u]) id[u] = node;
id[v] = node++;
}
}
if (node == 0) break;
for (int i = 0; i < N; ++i) if (id[i] == -1) id[i] = node++;
for (int i = 0; i < E; ++i) {
int u = e[i].from, v = e[i].to;
e[i].from = id[u], e[i].to = id[v];
if (e[i].from != e[i].to) e[i].w -= in[v];
}
N = node, root = id[root];//*
}
return res;
}
int main() {
freopen("HDU2121.in", "r", stdin);
freopen("HDU2121.out", "w", stdout);
while (scanf("%d %d", &n, &m) != EOF) {
cnt = 0, sum = 0;//*
for (int i = 1; i <= m; ++i) {
scanf("%d %d %d", &s, &t, &c);
add(s + 1, t + 1, c);
sum += c;
}
sum++;
for (int i = 1; i <= n; ++i) add(0, i, sum);//*
int ans = MST(0, n + 1, cnt);
if (ans == -1 || ans >= sum * 2) printf("impossible\n\n");//*
else printf("%d %d\n\n", ans - sum, mr - m);//*
}
return 0;
}
Summary
具體寫題目時的問題,已在代碼中標記出,簡單統計一下:
1、每組數據輸出後,還要輸出一個空行
2、不要忘記根節點到每一個節點的距離
3、每一次遞歸調用時,更新根節點應更新爲其對應的環的標號,而不是mr