传说中的带权有向图上的中国邮路问题。
【题目大意】
带权有向图上的中国邮路问题:一名邮递员需要经过每条有向边至少一次,最后回到出发点,一条边多次经过权值要累加,问最小总权值是多少。(2 <= N <= 100, 1 <= M <= 2000)
【建模方法】
若原图的基图不连通,或者存在某个点的入度或出度为0则无解。统计所有点的入度出度之差Di,对于Di > 0的点,加边(s, i, Di, 0);对于Di < 0的点,加边(i, t, -Di, 0);对原图中的每条边(i, j),在网络中加边(i, j, ∞, Dij),其中Dij为边(i, j)的权值。求一次最小费用流,费用加上原图所有边权和即为结果。
若进一步要求输出最小权值回路,则对所有流量fij > 0的边(i, j),在原图中复制fij份,这样原图便成为欧拉图,求一次欧拉回路即可。
以上摘自Edelweiss《网络流建模汇总》
启示:
这篇论文的前面还讲到一个混合图欧拉回路的问题(就是给出一张既含有有向边又含有无向边的图,问你是否能通过确定图中一些无向边的方向使得图中存在欧拉回路),这种涉及到回路的问题,都要抓住每个点的出度和入度这两个非常重要的条件来构图。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <queue> 6 #include <cstdlib> 7 #define maxn 110 8 #define maxm 5000 9 #define INF 1<<30 10 using namespace std; 11 12 struct MCMF{ 13 int src,sink,e,n; 14 int first[maxn]; 15 int cap[maxm],cost[maxm],u[maxm],v[maxm],next[maxm]; 16 bool flag; 17 void init(){ 18 e = 0; 19 memset(first,-1,sizeof(first)); 20 } 21 22 void add_edge(int a,int b,int cc,int ww){ 23 //printf("add:%d to %d,cap = %d,cost = %d ",a,b,cc,ww); 24 u[e] = a; 25 cap[e] = cc;cost[e] = ww;v[e] = b; 26 next[e] = first[a];first[a] = e++; 27 u[e] = b; 28 cap[e] = 0;cost[e] = -ww;v[e] = a; 29 next[e] = first[b];first[b] = e++; 30 } 31 32 int d[maxn],pre[maxn],pos[maxn]; 33 bool vis[maxn]; 34 35 bool spfa(int s,int t){ 36 memset(pre,-1,sizeof(pre)); 37 memset(vis,0,sizeof(vis)); 38 queue<int> Q; 39 for(int i = 0;i <= n;i++) d[i] = INF; 40 Q.push(s);pre[s] = s;d[s] = 0;vis[s] = 1; 41 while(!Q.empty()){ 42 int u = Q.front();Q.pop(); 43 vis[u] = 0; 44 for(int i = first[u];i != -1;i = next[i]){ 45 if(cap[i] > 0 && d[u] + cost[i] < d[v[i]]){ 46 d[v[i]] = d[u] + cost[i]; 47 pre[v[i]] = u;pos[v[i]] = i; 48 if(!vis[v[i]]) vis[v[i]] = 1,Q.push(v[i]); 49 } 50 } 51 } 52 return pre[t] != -1; 53 } 54 55 int Mincost; 56 int Maxflow; 57 58 int MinCostFlow(int s,int t,int nn){ 59 Mincost = 0,Maxflow = 0,n = nn; 60 while(spfa(s,t)){ 61 int min_f = INF; 62 for(int i = t;i != s;i = pre[i]) 63 if(cap[pos[i]] < min_f) min_f = cap[pos[i]]; 64 Mincost += d[t] * min_f; 65 Maxflow += min_f; 66 for(int i = t;i != s;i = pre[i]){ 67 cap[pos[i]] -= min_f; 68 cap[pos[i]^1] += min_f; 69 } 70 } 71 return Mincost; 72 } 73 }; 74 75 MCMF g; 76 int in[maxn],out[maxn]; 77 bool G[maxn][maxn]; 78 int N,M; 79 80 bool judge(){ 81 int i,j,k; 82 for(i = 1;i <= N;i++) G[i][i] = 1; 83 for(k = 1;k <= N;k++) 84 for(i = 1;i <= N;i++) 85 for(j = 1;j <= N;j++) 86 if(G[i][k] && G[k][j]) G[i][j] = 1; 87 for(i = 0;i < g.e;i += 2){ 88 int a = g.u[i],b = g.v[i]; 89 if(!(G[1][a] && G[b][1])) return false; 90 } 91 return true; 92 } 93 94 int main(){ 95 int kase; 96 scanf("%d",&kase); 97 while(kase--){ 98 g.init(); 99 memset(in,0,sizeof(in)); 100 memset(out,0,sizeof(out)); 101 memset(G,0,sizeof(G)); 102 scanf("%d%d",&N,&M); 103 int S = 0,T = N+1; 104 int ans = 0; 105 for(int i = 0;i < M;i++){ 106 int a,b,c; 107 scanf("%d%d%d",&a,&b,&c); 108 a++;b++; 109 G[a][b] = 1; 110 out[a]++;in[b]++; 111 g.add_edge(a,b,INF,c); 112 ans += c; 113 } 114 if(!judge()){ 115 printf("-1 "); 116 continue; 117 } 118 int sum = 0; 119 for(int i = 1;i <= N;i++){ 120 int d = in[i] - out[i]; 121 if(d > 0) g.add_edge(S,i,d,0); 122 else g.add_edge(i,T,-d,0); 123 sum += abs(d); 124 } 125 ans += g.MinCostFlow(S,T,T); 126 127 printf("%d ",ans); 128 } 129 return 0; 130 }