HIT2739 The Chinese Postman Problem(最小费用最大流)
2016-04-16 17:10
483 查看
题目大概说给一张有向图,要从0点出发返回0点且每条边至少都要走过一次,求走的最短路程。
经典的CPP问题,解法就是加边构造出欧拉回路,一个有向图存在欧拉回路的充分必要条件是基图连通且所有点入度等于出度。
而这题,果断联想到混合图欧拉回路的做法,用最小费用最大流解决:
先只考虑所有边都只走一次,计算出各个点的出度和入度,出度不等于入度的点就需要选择几条边去改变调整它们
对于出度多的就和容量网络的汇点连容量出度-入度费用0的边,入度多的源点就向其同样地连边
对于原图中的所有边<u,v>由u向v连容量INF费用该边长度的边,一单位流量流过该边就会使原本入度多的u点的出度+1,原本出度多的v点的入度+1
然后跑最小费用最大流,如果和源汇相关的边都满流那就有一个解了,而最后的解就是所有边的长度和+最小费用最大流的结果
另外注意判断基图连通。
经典的CPP问题,解法就是加边构造出欧拉回路,一个有向图存在欧拉回路的充分必要条件是基图连通且所有点入度等于出度。
而这题,果断联想到混合图欧拉回路的做法,用最小费用最大流解决:
先只考虑所有边都只走一次,计算出各个点的出度和入度,出度不等于入度的点就需要选择几条边去改变调整它们
对于出度多的就和容量网络的汇点连容量出度-入度费用0的边,入度多的源点就向其同样地连边
对于原图中的所有边<u,v>由u向v连容量INF费用该边长度的边,一单位流量流过该边就会使原本入度多的u点的出度+1,原本出度多的v点的入度+1
然后跑最小费用最大流,如果和源汇相关的边都满流那就有一个解了,而最后的解就是所有边的长度和+最小费用最大流的结果
另外注意判断基图连通。
#include<cstdio> #include<cstring> #include<queue> #include<algorithm> using namespace std; #define INF (1<<30) #define MAXN 111 #define MAXM 8888 struct Edge{ int u,v,cap,cost,next; }edge[MAXM]; int vs,vt,NV,NE,head[MAXN]; void addEdge(int u,int v,int cap,int cost){ edge[NE].u=u; edge[NE].v=v; edge[NE].cap=cap; edge[NE].cost=cost; edge[NE].next=head[u]; head[u]=NE++; edge[NE].u=v; edge[NE].v=u; edge[NE].cap=0; edge[NE].cost=-cost; edge[NE].next=head[v]; head[v]=NE++; } int d[MAXN],pre[MAXN]; bool vis[MAXN]; bool SPFA(){ for(int i=0; i<NV; ++i){ d[i]=INF; vis[i]=0; } d[vs]=0; vis[vs]=1; queue<int> que; que.push(vs); while(!que.empty()){ int u=que.front(); que.pop(); for(int i=head[u]; i!=-1; i=edge[i].next){ int v=edge[i].v; if(edge[i].cap && d[v]>d[u]+edge[i].cost){ d[v]=d[u]+edge[i].cost; pre[v]=i; if(!vis[v]){ vis[v]=1; que.push(v); } } } vis[u]=0; } return d[vt]!=INF; } int MCMF(int &mxflow){ int res=0; while(SPFA()){ int flow=INF,cost=0; for(int u=vt; u!=vs; u=edge[pre[u]].u){ flow=min(flow,edge[pre[u]].cap); } mxflow-=flow; for(int u=vt; u!=vs; u=edge[pre[u]].u){ edge[pre[u]].cap-=flow; edge[pre[u]^1].cap+=flow; cost+=flow*edge[pre[u]].cost; } res+=cost; } return res; } int par[MAXN]; int Find(int x){ while(x!=par[x]){ par[x]=par[par[x]]; x=par[x]; } return x; } bool Union(int a,int b){ int pa=Find(a),pb=Find(b); if(pa==pb) return 0; par[pb]=pa; return 1; } int deg[MAXN]; int main(){ int t,n,m,a,b,c; scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m); vs=n; vt=vs+1; NV=vt+1; NE=0; memset(head,-1,sizeof(head)); memset(deg,0,sizeof(deg)); for(int i=0; i<n; ++i) par[i]=i; int res=0,tot=n,mxflow=0; for(int i=0; i<m; ++i){ scanf("%d%d%d",&a,&b,&c); addEdge(a,b,INF,c); res+=c; ++deg[a]; --deg[b]; if(Union(a,b)) --tot; } if(tot!=1){ puts("-1"); continue; } for(int i=0; i<n; ++i){ if(deg[i]<0) addEdge(vs,i,-deg[i],0); else addEdge(i,vt,deg[i],0),mxflow+=deg[i]; } res+=MCMF(mxflow); if(mxflow!=0) puts("-1"); else printf("%d\n",res); } return 0; }
相关文章推荐
- 【转】DDMS中线程状态的说明
- java学习笔记之数组排序
- *[Lintcode] Subarray Sum
- 蓝桥杯 大数阶乘
- C/C++连接mysql数据库(vs)
- Vim使用技巧
- Light OJ 1393 - Crazy Calendar(博弈)
- 链表一大堆
- no grammar constraints (DTD or XML schema) 解决方法
- 用树状数组解决敌兵布阵
- 在Web/Phpstorm中设置连接FTP(附带:文件比较,上传下载,同步等)
- 安卓事件处理机制
- 产品经理FIRST:总结《小米参与感》
- OpenCV中IplImage与Mat代码风格比较
- CreateProcess的用法
- 加密之SHA,MD5
- ShellSort
- 求二叉树的叶节点
- 第四次_Activity传递参数
- 基于Flume的美团日志收集系统(二)改进和优化