题意:给一个图,图中有部分是向边,部分是无向边,要求判断是否存在欧拉回路,若存在,输出路径。

分析:欧拉回路的定义是,从某个点出发,每条边经过一次之后恰好回到出发点。

  无向边同样只能走一次,只是不限制方向而已,那么这个情况下就不能拆边。不妨先按照所给的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间要空行。

  1. #include <bits/stdc++.h>
  2. #define LL long long
  3. #define pii pair<int,int>
  4. #define INF 0x7f7f7f7f
  5. using namespace std;
  6. const int N=;
  7. vector<int> vect[N], vec[N], ans;
  8.  
  9. int can[N], notru[N], notchu[N], chu[N], ru[N], edge_cnt, sum_flow1, sum_flow2, vis[];
  10.  
  11. bool check(int n) //保证有解
  12. {
  13. for(int i=; i<=n; i++) if( ==(&(ru[i]+chu[i])) ) return false; //奇数个度
  14. for(int i=; i<=n; i++) //每个点可以补救。若不可改出度为100,入度为2,肯定不行。得补得上才行
  15. {
  16. if( can[i]-abs(notru[i]-notchu[i])>= ) continue;
  17. else return false;
  18. }
  19. return true;
  20. }
  21.  
  22. struct node //网络流用的边
  23. {
  24. int from;
  25. int to;
  26. int cap;
  27. int flow;
  28. int has;
  29. int isU;
  30. }edge[], edg[];
  31.  
  32. void add_node(int from,int to,int cap,int flow,int has)
  33. {
  34. edge[edge_cnt].from=from;
  35. edge[edge_cnt].to=to;
  36. edge[edge_cnt].cap=cap;
  37. edge[edge_cnt].flow=flow;
  38. edge[edge_cnt].has=has;
  39. vect[from].push_back(edge_cnt++);
  40. }
  41.  
  42. bool vis1[N], vis2[N];
  43. void build_graph(int n,int m) //根据无向边建图。
  44. {
  45. memset(vis1,,sizeof(vis1));
  46. memset(vis2,,sizeof(vis2));
  47. for(int i=; i<m; i++)
  48. {
  49. if(edg[i].isU)
  50. {
  51. int a=edg[i].from;
  52. int b=edg[i].to;
  53. add_node(a, b, , , i); //a的出度可给人
  54. add_node(b, a, , , i);
  55.  
  56. if(!vis1[a] && chu[a]>ru[a] ) //出度多,可流向别人
  57. {
  58. sum_flow1+=(chu[a]-ru[a])/;
  59. vis1[a]=;
  60. add_node(, a, (chu[a]-ru[a])/, , -); //源点-(出边多的点)
  61. add_node(a, , , , -);
  62. }
  63. if(!vis2[b] && ru[b]>chu[b]) //所有缺边的都连到汇点
  64. {
  65. sum_flow2+=(ru[b]-chu[b])/;
  66. vis2[b]=;
  67. add_node(b, n+, (ru[b]-chu[b])/, , - );
  68. add_node(n+, b, , , - );
  69. }
  70. }
  71. }
  72. }
  73.  
  74. int flow[N], path[N];
  75. int BFS(int s,int e)
  76. {
  77. deque<int> que(,s);
  78. flow[s]=INF;
  79. while(!que.empty())
  80. {
  81. int x=que.front();
  82. que.pop_front();
  83. for(int i=; i<vect[x].size(); i++)
  84. {
  85. node e=edge[vect[x][i]];
  86. if(!flow[e.to] && e.cap>e.flow )
  87. {
  88. flow[e.to]=min(flow[e.from],e.cap-e.flow );
  89. path[e.to]=vect[x][i];
  90. que.push_back(e.to);
  91. }
  92. }
  93. if(flow[e]) return flow[e];
  94. }
  95. return flow[e];
  96. }
  97.  
  98. int cal(int s,int e) //求最大流。只能满流有解
  99. {
  100. int ans_flow=;
  101. while(true)
  102. {
  103. memset(flow,,sizeof(flow));
  104. memset(path,,sizeof(path));
  105. int tmp=BFS(s,e);
  106. if(tmp==) return ans_flow;
  107. ans_flow+=tmp;
  108. int ed=e;
  109. while(ed!=s)
  110. {
  111. int t=path[ed];
  112. edge[t].flow+=tmp;
  113. edge[t^].flow-=tmp;
  114. ed=edge[t].from;
  115. }
  116. }
  117. }
  118.  
  119. void change_edge(int m) //改变边的方向,重新建邻接表。
  120. {
  121. for(int i=; i<edge_cnt; i+=)
  122. if(edge[i].has>= && edge[i].flow> ) //有流过的才需要改
  123. swap(edg[edge[i].has].from , edg[edge[i].has].to );
  124.  
  125. for(int i=; i<N; i++) vec[i].clear();
  126. for(int i=; i<m; i++) vec[edg[i].from].push_back(i); //重新建立临接表
  127. }
  128.  
  129. void fluery(int x) //任意一个点开始即可
  130. {
  131. for(int i=; i<vec[x].size(); i++)
  132. {
  133. int t=vec[x][i];
  134. if(!vis[t]) //该边没遍历过
  135. {
  136. vis[t]=;
  137. fluery(edg[t].to);
  138. }
  139. }
  140. ans.push_back(x);
  141. }
  142.  
  143. void init()
  144. {
  145. edge_cnt=;
  146. sum_flow1=;
  147. sum_flow2=;
  148. memset(can, , sizeof(can));
  149. memset(notru, , sizeof(notru));
  150. memset(notchu, , sizeof(notchu));
  151. memset(chu, , sizeof(chu));
  152. memset(ru, , sizeof(ru));
  153. memset(edge, , sizeof(edge));
  154. memset(edg, , sizeof(edg));
  155. for(int i=; i<N; i++)
  156. vec[i].clear(),vect[i].clear();
  157. }
  158.  
  159. int main()
  160. {
  161. freopen("input.txt", "r", stdin);
  162. int t, a, b, n, m;
  163. char c;
  164. cin>>t;
  165. while(t--)
  166. {
  167. init();
  168. scanf("%d%d",&n,&m);
  169. for(int i=; i<m; i++)
  170. {
  171. scanf("%d%d",&a,&b);
  172. while((c=getchar())==' ' );
  173. //cin>>c;
  174. //原图*************************
  175. edg[i].from=a;
  176. edg[i].to=b;
  177. edg[i].isU=(c=='U'?:);
  178. vec[a].push_back(i);
  179. //统计度***********************
  180. chu[a]++,ru[b]++; //总出入度
  181. if(c=='U') can[a]++,can[b]++; //保存无向边的度
  182. else notru[b]++,notchu[a]++; //登记有向边的出入度
  183. }
  184.  
  185. if(!check(n)) puts("No euler circuit exist"); //检查是否有解
  186. else
  187. {
  188. build_graph(n, m); //建临时图edge
  189. if(sum_flow1!=sum_flow2 || cal(, n+)!=sum_flow1 ) //增广路求最大流
  190. {
  191. puts("No euler circuit exist");
  192. if(t) printf("\n");
  193. continue;
  194. }
  195. change_edge(m); //改变有流的边,重建原图edg的邻接表
  196. memset(vis, , sizeof(vis));
  197. ans.clear();
  198. fluery(n); //求欧拉回路路径
  199. for(int i=ans.size()-; i>; i--) printf("%d ",ans[i]); //反向输出路径
  200. printf("%d\n",ans[]);
  201. }
  202. if(t) printf("\n");
  203. }
  204. return ;
  205. }

AC代码

UVA 10735 Euler Circuit 混合图的欧拉回路(最大流,fluery算法)的更多相关文章

  1. UVa 10735 - Euler Circuit(最大流 + 欧拉回路)

    链接: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...

  2. UVA 10735 Euler Circuit (最大流)

    题意:求混合图的欧拉路径. 一句话总结:网络流,最主要在于建图,此题是将出度则是和流量联系在了一起,用最大流来调整边的指向. 分析: 这题的困难之处在于无向边只能用一次,相当于一个方向未定的有向边. ...

  3. POJ 1637 混合图求欧拉回路 最大流实现

    前面讲过了无向图,有向图求欧拉回路,欧拉通路的做法.可以直接根据度数来判断,当然前提是这是一个连通图. 这道题既有无向边,又有有向边,然后求欧拉回路. 采用的方法是最大流. 具体处理方法. 首先,我们 ...

  4. 紫书 例题 11-13 UVa 10735(混合图的欧拉回路)(最大流)

    这道题写了两个多小时-- 首先讲一下怎么建模 我们的目的是让所有点的出度等于入度 那么我们可以把点分为两部分, 一部分出度大于入度, 一部分入度大于出度 那么显然, 按照书里的思路,将边方向后,就相当 ...

  5. bzoj2095: [Poi2010]Bridges(二分+混合图求欧拉回路)

    传送门 这篇题解讲的真吼->这里 首先我们可以二分一个答案,然后把所有权值小于这个答案的都加入图中 那么问题就转化为一张混合图(既有有向边又有无向边)中是否存在欧拉回路 首先 无向图存在欧拉回路 ...

  6. [POJ1637]混合图的欧拉回路判定|网络流

    混合图的欧拉回路判定 上一篇正好分别讲了有向图和无向图的欧拉回路判定方法 如果遇上了混合图要怎么做呢? 首先我们思考有向图的判定方法:所有点的出度=入度 我们可以先为无向边任意定一个向,算出此时所有顶 ...

  7. UVa 10735 (混合图的欧拉回路) Euler Circuit

    题意: 给出一个图,有的边是有向边,有的是无向边.试找出一条欧拉回路. 分析: 按照往常的思维,遇到混合图,我们一般会把无向边拆成两条方向相反的有向边. 但是在这里却行不通了,因为拆成两条有向边的话, ...

  8. UVA-10735 - Euler Circuit(混合欧拉回路输出)

    题意:给你一个图,有N个点,M条边,这M条边有的是单向的,有的是双向的. 问你能否找出一条欧拉回路,使得每条边都只经过一次! 分析: 下面转自别人的题解: 把该图的无向边随便定向,然后计算每个点的入度 ...

  9. ACM/ICPC 之 混合图的欧拉回路判定-网络流(POJ1637)

    //网络流判定混合图欧拉回路 //通过网络流使得各点的出入度相同则possible,否则impossible //残留网络的权值为可改变方向的次数,即n个双向边则有n次 //Time:157Ms Me ...

随机推荐

  1. JS利用正则配合replace替换指定字符

    替换指定字符的方法有很多,在本文为大家详细介绍下,JS利用正则配合replace是如何做到的,喜欢的朋友可以参考下 定义和用法 replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一 ...

  2. PHP对XML文件操作类讲解

    <?phpclass XML{    private $dom;        function __construct ()    {        $this->dom = new D ...

  3. Open Phone, SMS, Email, Skype and Browser apps of Android in Unity3d

    最近项目需要使用Android的一些基本功能,写插件各种悲剧,google了一下,如获至宝.Nice ! string url = String.Format("tel:{0}", ...

  4. Java Notes

    1.java是解释型语言.java虚拟机能实现一次编译多次运行. 2.JDK(java software Development kit 软件开发包),JRE(java Runtime Environ ...

  5. POJ 3440 Coin Toss(求概率)

    题目链接 题意 :把硬币往棋盘上扔,分别求出硬币占1,2,3,4个格子的时候的概率. 思路 : 求出公式输出,不过要注意输出格式,我还因为输入的时候用了int类型错了好几次..... #include ...

  6. hdu 4111 Alice and Bob 博弈论

    这里有2种方法: 方法一:求SG函数 sg[i][j]:i表示1的个数,j表示合并操作的步数. 这共有4种操作: 1.消除一个1: 2.减掉一个1: 3.合并2个1: 4.把1合并到另外不是1中. 代 ...

  7. utf-8中的汉字占用多少字节

    转载:http://blog.csdn.net/chummyhe89/article/details/7777613 占2个字节的:〇 占3个字节的:基本等同于GBK,含21000多个汉字 占4个字节 ...

  8. GC垃圾回收之GC.KeepAlive方法

    http://msdn.microsoft.com/zh-cn/library/system.gc.keepalive.aspx http://www.cnblogs.com/ren700622/ar ...

  9. mysql怎样建表及mysql优化

    1.符合数据库三范式 2.字段选择合适的数据类型 3.注意表之间的联系,一对多,多对多,一对一 4.拆分表,把不常用的字段单独成表. 5.建立索引,哪些字段建立索引?建立索引的原则?最左前缀原则,wh ...

  10. Hello World---C/C++

    C #include <stdio.h> void main() { printf("Hello World!\n"); } C++ #include <iost ...