• 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
    ",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 }
    View Code
  • 相关阅读:
    iptables之NAT端口转发设置
    Windows server 2008 R2远程桌面3389端口号修改
    Nginx启动错误:error while loading shared libraries: libpcre.so.1
    sp_change_users_login 'Update_One', '用户名', '登录名';
    Sqlserver 数据库定时自动备份
    ES DSL 基础查询语法学习笔记
    kafka命令使用
    kafka集群中常见错误的解决方法:kafka.common.KafkaException: Should not set log end offset on partition
    快速排序法(双冒泡排序法)
    运算符
  • 原文地址:https://www.cnblogs.com/zhexipinnong/p/3354506.html
Copyright © 2020-2023  润新知