题意:
给出一个有向带权图,求从起点到终点的两条不相交路径使得权值和最小。
分析:
第一次听到“拆点法”这个名词。
把除起点和终点以外的点拆成两个点i和i',然后在这两点之间连一条容量为1,费用为0的边。这样就保证了每个点最多经过一次。
其他有向边的容量也是1
然后求从起点到终点的流量为2(这样就保证了是两条路径)的最小费用流。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 const int maxn = 2000 + 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 MCMF 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_limit, 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 46 queue<int> Q; 47 Q.push(s); 48 while(!Q.empty()) 49 { 50 int u = Q.front(); Q.pop(); 51 inq[u] = 0; 52 for(int i = 0; i < G[u].size(); ++i) 53 { 54 Edge& e = edges[G[u][i]]; 55 if(e.cap > e.flow && d[e.to] > d[u] + e.cost) 56 { 57 d[e.to] = d[u] + e.cost; 58 p[e.to] = G[u][i]; 59 a[e.to] = min(a[u], e.cap - e.flow); 60 if(!inq[e.to]) { Q.push(e.to); inq[e.to] = 1; } 61 } 62 } 63 } 64 if(d[t] == INF) return false; 65 if(flow + a[t] > flow_limit) a[t] = flow_limit - flow; 66 flow += a[t]; 67 cost += d[t] * a[t]; 68 for(int u = t; u != s; u = edges[p[u]].from) 69 { 70 edges[p[u]].flow += a[t]; 71 edges[p[u]^1].flow -= a[t]; 72 } 73 return true; 74 } 75 76 int MincostMaxflow(int s, int t, int flow_limit, int& cost) 77 { 78 int flow = 0; cost = 0; 79 while(flow < flow_limit && BellmanFord(s, t, flow_limit, flow, cost)); 80 return flow; 81 } 82 }g; 83 84 int main() 85 { 86 //freopen("in.txt", "r", stdin); 87 88 int n, m; 89 while(scanf("%d%d", &n, &m) == 2 && n) 90 { 91 g.Init(n*2-2); 92 //2~n-1 i和i'的编号分别为1~n-2 n~2n-3 93 for(int i = 2; i <= n-1; ++i) g.AddEdge(i-1, n-2+i, 1, 0); 94 for(int i = 0; i < m; ++i) 95 { //连接a'->b 96 int a, b, c; 97 scanf("%d%d%d", &a, &b, &c); 98 if(a != 1 && a != n) a += n-2; else a--; 99 b--; 100 g.AddEdge(a, b, 1, c); 101 } 102 int cost; 103 g.MincostMaxflow(0, n-1, 2, cost); 104 printf("%d\n", cost); 105 } 106 107 return 0; 108 }