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

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

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

    Selenium官网:http://www.seleniumhq.org/ Selenium火狐插件地址:http://release.seleniumhq.org/selenium-ide/ 浏览器 ...

  2. How does database indexing work?

    When data is stored on disk based storage devices, it is stored as blocks of data. These blocks are ...

  3. POJ 1785 Binary Search Heap Construction(裸笛卡尔树的构造)

    笛卡尔树: 每个节点有2个关键字key.value.从key的角度看,这是一颗二叉搜索树,每个节点的左子树的key都比它小,右子树都比它大:从value的角度看,这是一个堆. 题意:以字符串为关键字k ...

  4. POJ2965The Pilots Brothers' refrigerator

    http://poj.org/problem?id=2965 这个题的话,一开始也不会做,旁边的人说用BFS,后来去网上看了众大神的思路,瞬间觉得用BFS挺简单易:因为要让一个“+”变为“-”,只要将 ...

  5. Ubuntu环境下利用ant编译nutch2.2.1 & 配置nutch2.2.1

    /×××××××××××××××××××××××××××××××××××××××××/ Author:xxx0624 HomePage:http://www.cnblogs.com/xxx0624/ ...

  6. Spring 3.0: Unable to locate Spring NamespaceHandler for XML schema namespace

    被这个问题折磨着很久:参考: http://have23.iteye.com/blog/1340777 (cfx 与 spring 整合的时候出现的问题: org.springframework.be ...

  7. linux网络配置、环境变量以及JDK安装(CentOS 6.5)

    由于需要搭建hadoop平台,但是苦于没有现成可用的linux服务器,只好自己下载了CentOS 6.5从头装起,安装过程中遇到了很多问题,比如网络配置.时钟同步.环境变量配置.以及各种服务的启停,还 ...

  8. Project Euler 86:Cuboid route 长方体路径

    Cuboid route A spider, S, sits in one corner of a cuboid room, measuring 6 by 5 by 3, and a fly, F, ...

  9. lintcode:子数组之和为0

    题目: 子数组之和 给定一个整数数组,找到和为零的子数组.你的代码应该返回满足要求的子数组的起始位置和结束位置 样例 给出[-3, 1, 2, -3, 4],返回[0, 2] 或者 [1, 3]. 解 ...

  10. GuessFist

    import java.util.Scanner; import java.util.Random; /** *跟电脑玩石头剪刀布,需要从控制台输入信息, *然后去判断,然后给予反馈信息 */ pub ...