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

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

  无向边同样只能走一次,只是不限制方向而已,那么这个情况下就不能拆边。不妨先按照所给的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=;
vector<int> vect[N], vec[N], ans; int can[N], notru[N], notchu[N], chu[N], ru[N], edge_cnt, sum_flow1, sum_flow2, vis[]; bool check(int n) //保证有解
{
for(int i=; i<=n; i++) if( ==(&(ru[i]+chu[i])) ) return false; //奇数个度
for(int i=; i<=n; i++) //每个点可以补救。若不可改出度为100,入度为2,肯定不行。得补得上才行
{
if( can[i]-abs(notru[i]-notchu[i])>= ) continue;
else return false;
}
return true;
} struct node //网络流用的边
{
int from;
int to;
int cap;
int flow;
int has;
int isU;
}edge[], edg[]; 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[N], vis2[N];
void build_graph(int n,int m) //根据无向边建图。
{
memset(vis1,,sizeof(vis1));
memset(vis2,,sizeof(vis2));
for(int i=; i<m; i++)
{
if(edg[i].isU)
{
int a=edg[i].from;
int b=edg[i].to;
add_node(a, b, , , i); //a的出度可给人
add_node(b, a, , , i); if(!vis1[a] && chu[a]>ru[a] ) //出度多,可流向别人
{
sum_flow1+=(chu[a]-ru[a])/;
vis1[a]=;
add_node(, a, (chu[a]-ru[a])/, , -); //源点-(出边多的点)
add_node(a, , , , -);
}
if(!vis2[b] && ru[b]>chu[b]) //所有缺边的都连到汇点
{
sum_flow2+=(ru[b]-chu[b])/;
vis2[b]=;
add_node(b, n+, (ru[b]-chu[b])/, , - );
add_node(n+, b, , , - );
}
}
}
} int flow[N], path[N];
int BFS(int s,int e)
{
deque<int> que(,s);
flow[s]=INF;
while(!que.empty())
{
int x=que.front();
que.pop_front();
for(int i=; 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=;
while(true)
{
memset(flow,,sizeof(flow));
memset(path,,sizeof(path));
int tmp=BFS(s,e);
if(tmp==) return ans_flow;
ans_flow+=tmp;
int ed=e;
while(ed!=s)
{
int t=path[ed];
edge[t].flow+=tmp;
edge[t^].flow-=tmp;
ed=edge[t].from;
}
}
} void change_edge(int m) //改变边的方向,重新建邻接表。
{
for(int i=; i<edge_cnt; i+=)
if(edge[i].has>= && edge[i].flow> ) //有流过的才需要改
swap(edg[edge[i].has].from , edg[edge[i].has].to ); for(int i=; i<N; i++) vec[i].clear();
for(int i=; i<m; i++) vec[edg[i].from].push_back(i); //重新建立临接表
} void fluery(int x) //任意一个点开始即可
{
for(int i=; i<vec[x].size(); i++)
{
int t=vec[x][i];
if(!vis[t]) //该边没遍历过
{
vis[t]=;
fluery(edg[t].to);
}
}
ans.push_back(x);
} void init()
{
edge_cnt=;
sum_flow1=;
sum_flow2=;
memset(can, , sizeof(can));
memset(notru, , sizeof(notru));
memset(notchu, , sizeof(notchu));
memset(chu, , sizeof(chu));
memset(ru, , sizeof(ru));
memset(edge, , sizeof(edge));
memset(edg, , sizeof(edg));
for(int i=; 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=; 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'?:);
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(, n+)!=sum_flow1 ) //增广路求最大流
{
puts("No euler circuit exist");
if(t) printf("\n");
continue;
}
change_edge(m); //改变有流的边,重建原图edg的邻接表
memset(vis, , sizeof(vis));
ans.clear();
fluery(n); //求欧拉回路路径
for(int i=ans.size()-; i>; i--) printf("%d ",ans[i]); //反向输出路径
printf("%d\n",ans[]);
}
if(t) printf("\n");
}
return ;
}

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. 【BZOJ】【1770】【Usaco2009 Nov】lights 灯

    高斯消元解XOR方程组 一眼看上去是高斯消元解xor方程组……但是不会写……sad 去膜拜了Hzwer和ZYF Hzwer啥也没说,还是zyf靠谱…… 当多解的时候就需要爆搜枚举自由元的情况,找最优解 ...

  2. action间传多个参数时注意问题

    通常我们action之间传参可以有多种形式,举例说明:示例1: <result name="test" type="redirect-action"> ...

  3. 剑指offer--面试题15--相关

    感受:清晰的思路是最重要的!!! 题目:求链表中间节点 ListNode* MidNodeInList(ListNode* pHead) { if(pHead == NULL) return NULL ...

  4. Application、Session、Cookie、ViewState的特性

    http://blog.csdn.net/zyw_anquan/article/details/7664132   Application的特性: 存储的物理位置:服务器端内存. 存储的类型限制:任意 ...

  5. leetcode Largest Rectangle in Histogram 解法二

    上一篇文章讲了该题的一个解法.后来又发现一个更好的解法. 首先依旧考虑一个升序的数列,例如1,2,3,4,5.那么它的最大矩形显然是有5种可能,即 1*5,2*4,3*3,4*2,1*5.所以最大的矩 ...

  6. 三层架构和MVC

    注:本文章内所有内容都来自互联网,本人主要是起了一个收集的作用 http://www.cnblogs.com/zhhh/archive/2011/06/10/2077519.html 又看到有人在问三 ...

  7. POJ 1094 Sorting It All Out (拓扑排序,判断序列是否唯一,图是否有环)

    题意:给出n个字符,m对关系,让你输出三种情况:     1.若到第k行时,能判断出唯一的拓扑序列,则输出:         Sorted sequence determined after k re ...

  8. 不要将缓存服务器与Tomcat放在单台机器上,否则出现竞争内存问题

    缓存分为本地缓存和远程分布式缓存,本地缓存访问速度更快但缓存数据量有限,同时存在与应用程序争用内存的情况. 1.不要将缓存服务器与Tomcat放在单台机器上,否则出现竞争内存问题 2.不要将缓存服务器 ...

  9. iOS开发--使用NSMutableAttributedString 实现富文本

    在iOS开发中,常常会有一段文字显示不同的颜色和字体,或者给某几个文字加删除线或下划线的需求.之前在网上找了一些资料,有的是重绘UILabel的textLayer,有的是用html5实现的,都比较麻烦 ...

  10. java--依赖、关联、聚合和组合之间区别的理解

    在学习面向对象设计对象关系时,依赖.关联.聚合和组合这四种关系之间区别比较容易混淆.特别是后三种,仅仅是在语义上有所区别,所谓语义就是指上下文环境.特定情景等. 依赖(Dependency)关系是类与 ...