题意:
给出一个有向带权图,找到若干个圈,使得每个点恰好属于一个圈。而且这些圈所有边的权值之和最小。
分析:
每个点恰好属于一个有向圈 就等价于 每个点都有唯一后继。
所以把每个点i拆成两个点,Xi 和 Yi ,然后求二分图最小权完美匹配(流量为n也就是满载时,就是完美匹配)。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 const int maxn = 200 + 10; 6 const int INF = 1000000000; 7 8 struct Edge 9 { 10 int from, to, cap, flow, cost; 11 Edge(int u, int v, int c, int f, int w): from(u), to(v), cap(c), flow(f), cost(w) {} 12 }; 13 14 struct MFMC 15 { 16 int n, m; 17 vector<Edge> edges; 18 vector<int> G[maxn]; 19 int inq[maxn]; //是否在队列中 20 int d[maxn]; //Bellman-Ford 21 int p[maxn]; //上一条弧 22 int a[maxn]; //可改进量 23 24 void Init(int n) 25 { 26 this->n = n; 27 for(int i = 0; i < n; ++i) G[i].clear(); 28 edges.clear(); 29 } 30 31 void AddEdge(int from, int to, int cap, int cost) 32 { 33 edges.push_back(Edge(from, to, cap, 0, cost)); 34 edges.push_back(Edge(to, from, 0, 0, -cost)); 35 m = edges.size(); 36 G[from].push_back(m-2); 37 G[to].push_back(m-1); 38 } 39 40 bool BellmanFord(int s, int t, int& flow, int& cost) 41 { 42 for(int i = 0; i < n; ++i) d[i] = INF; 43 memset(inq, 0, sizeof(inq)); 44 d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF; 45 queue<int> Q; 46 Q.push(s); 47 while(!Q.empty()) 48 { 49 int u = Q.front(); Q.pop(); 50 inq[u] = 0; 51 for(int i = 0; i < G[u].size(); ++i) 52 { 53 Edge& e = edges[G[u][i]]; 54 if(e.cap > e.flow && d[e.to] > d[u] + e.cost) 55 { 56 d[e.to] = d[u] + e.cost; 57 p[e.to] = G[u][i]; 58 a[e.to] = min(a[u], e.cap - e.flow); 59 if(!inq[e.to]) { Q.push(e.to); inq[e.to] = 1; } 60 } 61 } 62 } 63 if(d[t] == INF) return false; 64 flow += a[t]; 65 cost += d[t] * a[t]; 66 for(int u = t; u != s; u = edges[p[u]].from) 67 { 68 edges[p[u]].flow += a[t]; 69 edges[p[u]^1].flow -= a[t]; 70 } 71 return true; 72 } 73 //需要保证初始网络中没有负权圈 74 int MincostMaxflow(int s, int t, int& cost) 75 { 76 int flow = 0; cost = 0; 77 while(BellmanFord(s, t, flow, cost)); 78 return flow; 79 } 80 }g; 81 82 int main() 83 { 84 //freopen("in.txt", "r", stdin); 85 86 int n; 87 while(scanf("%d", &n) == 1 && n) 88 { 89 g.Init(n*2+2); 90 for(int i = 1; i <= n; ++i) 91 { 92 g.AddEdge(0, i, 1, 0);//连接源点和X的点 93 g.AddEdge(i+n, n*2+1, 1, 0);//连接汇点和Y的点 94 } 95 for(int i = 1; i <= n; ++i) 96 { 97 int j, d; 98 while(scanf("%d", &j) == 1 && j) 99 { 100 scanf("%d", &d); 101 g.AddEdge(i, n+j, 1, d); 102 } 103 } 104 int cost, flow; 105 flow = g.MincostMaxflow(0, n*2+1, cost); 106 if(flow < n) puts("N"); 107 else printf("%d ", cost); 108 } 109 110 return 0; 111 }