HOJ 2739 The Chinese Postman Problem

传说中的带权有向图上的中国邮路问题。

【题目大意】 
带权有向图上的中国邮路问题:一名邮递员需要经过每条有向边至少一次,最后回到出发点,一条边多次经过权值要累加,问最小总权值是多少。(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\n",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\n");
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\n",ans);
128     }
129     return 0;
130 }
View Code

 

转载于:https://www.cnblogs.com/zhexipinnong/p/3354506.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值