HIT2739 The Chinese Postman Problem(最小费用最大流)

题目大概说给一张有向图,要从0点出发返回0点且每条边至少都要走过一次,求走的最短路程。

经典的CPP问题,解法就是加边构造出欧拉回路,一个有向图存在欧拉回路的充分必要条件是基图连通且所有点入度等于出度

而这题,果断联想到混合图欧拉回路的做法,用最小费用最大流解决:

  • 先只考虑所有边都只走一次,计算出各个点的出度和入度,出度不等于入度的点就需要选择几条边去改变调整它们
  • 对于出度多的就和容量网络的汇点连容量出度-入度费用0的边,入度多的源点就向其同样地连边
  • 对于原图中的所有边<u,v>由u向v连容量INF费用该边长度的边,一单位流量流过该边就会使原本入度多的u点的出度+1,原本出度多的v点的入度+1
  • 然后跑最小费用最大流,如果和源汇相关的边都满流那就有一个解了,而最后的解就是所有边的长度和+最小费用最大流的结果

另外注意判断基图连通。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<queue>
  4 #include<algorithm>
  5 using namespace std;
  6 #define INF (1<<30)
  7 #define MAXN 111
  8 #define MAXM 8888
  9 struct Edge{
 10     int u,v,cap,cost,next;
 11 }edge[MAXM];
 12 int vs,vt,NV,NE,head[MAXN];
 13 void addEdge(int u,int v,int cap,int cost){
 14     edge[NE].u=u; edge[NE].v=v; edge[NE].cap=cap; edge[NE].cost=cost;
 15     edge[NE].next=head[u]; head[u]=NE++;
 16     edge[NE].u=v; edge[NE].v=u; edge[NE].cap=0; edge[NE].cost=-cost;
 17     edge[NE].next=head[v]; head[v]=NE++;
 18 }
 19 int d[MAXN],pre[MAXN];
 20 bool vis[MAXN];
 21 bool SPFA(){
 22     for(int i=0; i<NV; ++i){
 23         d[i]=INF; vis[i]=0;
 24     }
 25     d[vs]=0; vis[vs]=1;
 26     queue<int> que;
 27     que.push(vs);
 28     while(!que.empty()){
 29         int u=que.front(); que.pop();
 30         for(int i=head[u]; i!=-1; i=edge[i].next){
 31             int v=edge[i].v;
 32             if(edge[i].cap && d[v]>d[u]+edge[i].cost){
 33                 d[v]=d[u]+edge[i].cost;
 34                 pre[v]=i;
 35                 if(!vis[v]){
 36                     vis[v]=1;
 37                     que.push(v);
 38                 }
 39             }
 40         }
 41         vis[u]=0;
 42     }
 43     return d[vt]!=INF;
 44 }
 45 int MCMF(int &mxflow){
 46     int res=0;
 47     while(SPFA()){
 48         int flow=INF,cost=0;
 49         for(int u=vt; u!=vs; u=edge[pre[u]].u){
 50             flow=min(flow,edge[pre[u]].cap);
 51         }
 52         mxflow-=flow;
 53         for(int u=vt; u!=vs; u=edge[pre[u]].u){
 54             edge[pre[u]].cap-=flow;
 55             edge[pre[u]^1].cap+=flow;
 56             cost+=flow*edge[pre[u]].cost;
 57         }
 58         res+=cost;
 59     }
 60     return res;
 61 }
 62 int par[MAXN];
 63 int Find(int x){
 64     while(x!=par[x]){
 65         par[x]=par[par[x]];
 66         x=par[x];
 67     }
 68     return x;
 69 }
 70 bool Union(int a,int b){
 71     int pa=Find(a),pb=Find(b);
 72     if(pa==pb) return 0;
 73     par[pb]=pa;
 74     return 1;
 75 }
 76 int deg[MAXN];
 77 int main(){
 78     int t,n,m,a,b,c;
 79     scanf("%d",&t);
 80     while(t--){
 81         scanf("%d%d",&n,&m);
 82         vs=n; vt=vs+1; NV=vt+1; NE=0;
 83         memset(head,-1,sizeof(head));
 84         memset(deg,0,sizeof(deg));
 85         for(int i=0; i<n; ++i) par[i]=i;
 86         int res=0,tot=n,mxflow=0;
 87         for(int i=0; i<m; ++i){
 88             scanf("%d%d%d",&a,&b,&c);
 89             addEdge(a,b,INF,c);
 90             res+=c;
 91             ++deg[a]; --deg[b];
 92             if(Union(a,b)) --tot;
 93         }
 94         if(tot!=1){
 95             puts("-1");
 96             continue;
 97         }
 98         for(int i=0; i<n; ++i){
 99             if(deg[i]<0) addEdge(vs,i,-deg[i],0);
100             else addEdge(i,vt,deg[i],0),mxflow+=deg[i];
101         }
102         res+=MCMF(mxflow);
103         if(mxflow!=0) puts("-1");
104         else printf("%d\n",res);
105     }
106     return 0;
107 }

 

转载于:https://www.cnblogs.com/WABoss/p/5398810.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值