UVA 10735 Euler Circuit 混合图的欧拉回路(最大流,fluery算法)
2015-07-18 11:32
357 查看
题意:给一个图,图中有部分是向边,部分是无向边,要求判断是否存在欧拉回路,若存在,输出路径。
分析:欧拉回路的定义是,从某个点出发,每条边经过一次之后恰好回到出发点。
无向边同样只能走一次,只是不限制方向而已,那么这个情况下就不能拆边。不妨先按照所给的start和end的顺序,初步定下该无向边的顺序(若不当,一会再改)。那么有个问题,我们需要先判断其是否存在欧拉回路先。
混合图不满足欧拉回路因素有:(1)一个点的度(无论有无向)是奇数的,那么其肯定不能满足出边数等于入边数。(2)有向边的出入度过于悬殊(悬殊是指,拿所有无向边来怎么抵消都是不平衡)。
首先可以先将不满足上述两个条件的case结束掉,无解。
那么还有个问题,这样随便地定下一条无向边为随意一个方向不是太随意了吗?当然,这样做不可能保证每个点的出入度就平衡了,所以我们得想办法让每个点的出入度都平衡。考虑到,如果一个点的出度多了,那么该点可以将多出的部分出度换回来入度,这相当于将出度“流向”其他需要出度的点。好像可以最大流解决,好吧,接下来讲建图。
建图。对于任意一条无向边(被我们已经随意定向的那些),假设其方向初定位u-v。那么u有出度可以赠人了,所以新图中u到v有边,容量是1,表示将出度送给v,当然自己就会获得入度1个。但是并不是所有的点u的出度都很多以至于可以送人,不过其出度可以赠人这倒是没错的,看它肯不肯了。当chudu[u]>rudu[u]时,必定可以给人,且可以给(chudu[u]-rudu[u])/2,这很明显。那么应该有边从源点到u,容量为(chudu[u]-rudu[u])/2,表示其可以流出这么多个出度(注意别重复建边)。同理,对于缺入度的点,应该有边到汇点,容量为(rudu[v]-chudu[v])/2。这样图就建成。
建完图后还要再判断一次是否有解,即当从源点出来的容量和到达汇点的容量不一致时,无解。因为缺入度的点和缺出度的点数不一样多,平衡不了。
若有解,再对新图来求一次最大流,最大流应该等于源点到其他点的容量之和。及所有点都平衡好了。所有有flow>0的边都是需要反过来的。全部在原图上修改后,重新建邻接表,进行求欧拉回路路径,这个用fluery算法即可。
特别需要注意的是:2个case间要空行。
AC代码
分析:欧拉回路的定义是,从某个点出发,每条边经过一次之后恰好回到出发点。
无向边同样只能走一次,只是不限制方向而已,那么这个情况下就不能拆边。不妨先按照所给的start和end的顺序,初步定下该无向边的顺序(若不当,一会再改)。那么有个问题,我们需要先判断其是否存在欧拉回路先。
混合图不满足欧拉回路因素有:(1)一个点的度(无论有无向)是奇数的,那么其肯定不能满足出边数等于入边数。(2)有向边的出入度过于悬殊(悬殊是指,拿所有无向边来怎么抵消都是不平衡)。
首先可以先将不满足上述两个条件的case结束掉,无解。
那么还有个问题,这样随便地定下一条无向边为随意一个方向不是太随意了吗?当然,这样做不可能保证每个点的出入度就平衡了,所以我们得想办法让每个点的出入度都平衡。考虑到,如果一个点的出度多了,那么该点可以将多出的部分出度换回来入度,这相当于将出度“流向”其他需要出度的点。好像可以最大流解决,好吧,接下来讲建图。
建图。对于任意一条无向边(被我们已经随意定向的那些),假设其方向初定位u-v。那么u有出度可以赠人了,所以新图中u到v有边,容量是1,表示将出度送给v,当然自己就会获得入度1个。但是并不是所有的点u的出度都很多以至于可以送人,不过其出度可以赠人这倒是没错的,看它肯不肯了。当chudu[u]>rudu[u]时,必定可以给人,且可以给(chudu[u]-rudu[u])/2,这很明显。那么应该有边从源点到u,容量为(chudu[u]-rudu[u])/2,表示其可以流出这么多个出度(注意别重复建边)。同理,对于缺入度的点,应该有边到汇点,容量为(rudu[v]-chudu[v])/2。这样图就建成。
建完图后还要再判断一次是否有解,即当从源点出来的容量和到达汇点的容量不一致时,无解。因为缺入度的点和缺出度的点数不一样多,平衡不了。
若有解,再对新图来求一次最大流,最大流应该等于源点到其他点的容量之和。及所有点都平衡好了。所有有flow>0的边都是需要反过来的。全部在原图上修改后,重新建邻接表,进行求欧拉回路路径,这个用fluery算法即可。
特别需要注意的是:2个case间要空行。
#include <bits/stdc++.h> #define LL long long #define pii pair<int,int> #define INF 0x7f7f7f7f using namespace std; const int N=150; vector<int> vect , vec , ans; int can , notru , notchu , chu , ru , edge_cnt, sum_flow1, sum_flow2, vis[550]; bool check(int n) //保证有解 { for(int i=1; i<=n; i++) if( 1==(1&(ru[i]+chu[i])) ) return false; //奇数个度 for(int i=1; i<=n; i++) //每个点可以补救。若不可改出度为100,入度为2,肯定不行。得补得上才行 { if( can[i]-abs(notru[i]-notchu[i])>=0 ) continue; else return false; } return true; } struct node //网络流用的边 { int from; int to; int cap; int flow; int has; int isU; }edge[4000], edg[550]; void add_node(int from,int to,int cap,int flow,int has) { edge[edge_cnt].from=from; edge[edge_cnt].to=to; edge[edge_cnt].cap=cap; edge[edge_cnt].flow=flow; edge[edge_cnt].has=has; vect[from].push_back(edge_cnt++); } bool vis1 , vis2 ; void build_graph(int n,int m) //根据无向边建图。 { memset(vis1,0,sizeof(vis1)); memset(vis2,0,sizeof(vis2)); for(int i=0; i<m; i++) { if(edg[i].isU) { int a=edg[i].from; int b=edg[i].to; add_node(a, b, 1, 0, i); //a的出度可给人 add_node(b, a, 0, 0, i); if(!vis1[a] && chu[a]>ru[a] ) //出度多,可流向别人 { sum_flow1+=(chu[a]-ru[a])/2; vis1[a]=1; add_node(0, a, (chu[a]-ru[a])/2, 0, -1); //源点-(出边多的点) add_node(a, 0, 0, 0, -1); } if(!vis2[b] && ru[b]>chu[b]) //所有缺边的都连到汇点 { sum_flow2+=(ru[b]-chu[b])/2; vis2[b]=1; add_node(b, n+1, (ru[b]-chu[b])/2, 0, -1 ); add_node(n+1, b, 0, 0, -1 ); } } } } int flow , path ; int BFS(int s,int e) { deque<int> que(1,s); flow[s]=INF; while(!que.empty()) { int x=que.front(); que.pop_front(); for(int i=0; i<vect[x].size(); i++) { node e=edge[vect[x][i]]; if(!flow[e.to] && e.cap>e.flow ) { flow[e.to]=min(flow[e.from],e.cap-e.flow ); path[e.to]=vect[x][i]; que.push_back(e.to); } } if(flow[e]) return flow[e]; } return flow[e]; } int cal(int s,int e) //求最大流。只能满流有解 { int ans_flow=0; while(true) { memset(flow,0,sizeof(flow)); memset(path,0,sizeof(path)); int tmp=BFS(s,e); if(tmp==0) return ans_flow; ans_flow+=tmp; int ed=e; while(ed!=s) { int t=path[ed]; edge[t].flow+=tmp; edge[t^1].flow-=tmp; ed=edge[t].from; } } } void change_edge(int m) //改变边的方向,重新建邻接表。 { for(int i=0; i<edge_cnt; i+=2) if(edge[i].has>=0 && edge[i].flow>0 ) //有流过的才需要改 swap(edg[edge[i].has].from , edg[edge[i].has].to ); for(int i=0; i<N; i++) vec[i].clear(); for(int i=0; i<m; i++) vec[edg[i].from].push_back(i); //重新建立临接表 } void fluery(int x) //任意一个点开始即可 { for(int i=0; i<vec[x].size(); i++) { int t=vec[x][i]; if(!vis[t]) //该边没遍历过 { vis[t]=1; fluery(edg[t].to); } } ans.push_back(x); } void init() { edge_cnt=0; sum_flow1=0; sum_flow2=0; memset(can, 0, sizeof(can)); memset(notru, 0, sizeof(notru)); memset(notchu, 0, sizeof(notchu)); memset(chu, 0, sizeof(chu)); memset(ru, 0, sizeof(ru)); memset(edge, 0, sizeof(edge)); memset(edg, 0, sizeof(edg)); for(int i=0; i<N; i++) vec[i].clear(),vect[i].clear(); } int main() { freopen("input.txt", "r", stdin); int t, a, b, n, m; char c; cin>>t; while(t--) { init(); scanf("%d%d",&n,&m); for(int i=0; i<m; i++) { scanf("%d%d",&a,&b); while((c=getchar())==' ' ); //cin>>c; //原图************************* edg[i].from=a; edg[i].to=b; edg[i].isU=(c=='U'?1:0); vec[a].push_back(i); //统计度*********************** chu[a]++,ru[b]++; //总出入度 if(c=='U') can[a]++,can[b]++; //保存无向边的度 else notru[b]++,notchu[a]++; //登记有向边的出入度 } if(!check(n)) puts("No euler circuit exist"); //检查是否有解 else { build_graph(n, m); //建临时图edge if(sum_flow1!=sum_flow2 || cal(0, n+1)!=sum_flow1 ) //增广路求最大流 { puts("No euler circuit exist"); if(t) printf("\n"); continue; } change_edge(m); //改变有流的边,重建原图edg的邻接表 memset(vis, 0, sizeof(vis)); ans.clear(); fluery(n); //求欧拉回路路径 for(int i=ans.size()-1; i>0; i--) printf("%d ",ans[i]); //反向输出路径 printf("%d\n",ans[0]); } if(t) printf("\n"); } return 0; }
AC代码
相关文章推荐
- UITextField、UILabel和 UITextView四个容易混淆的属性
- Hadoop 管理工具HUE配置
- Win10 RTM Build 10240准正式版简体中文ISO镜像下载 (附KMS激活密钥)
- String,StringBuffer与StringBuilder的区别??
- request.getParameter(param) 的中文乱码问题
- egret GUI 和 egret Wing 是我看到h5 最渣的设计
- android BuildConfig Debug的妙用
- ios uitableview group模式顶部有个空白
- ERROR/Zygote(33): setreuid() failed
- UINavigationController与UITabbarController的样式
- select, iocp, epoll,kqueue及各种I/O复用机制
- [UEFI启动教程][第三章]BIOS锁定纯UEFI启动的解锁办法
- Arduino Pro or Pro Mini, ATmega328 (5V, 16 MHz)成功烧录方法
- handsontable-developer guide-cell function
- handsontable-developer guide-cell editor
- handsontable-developer guide-cell type
- handsontable-developer guide-setting options,callback
- handsontable-developer guide-load and save
- handsontable-developer guide-data binding,data sources
- [LeetCode]Implement Queue using Stacks