UVA 10735 Euler Circuit 混合图的欧拉回路(最大流,fluery算法)

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

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

  无向边同样只能走一次,只是不限制方向而已,那么这个情况下就不能拆边。不妨先按照所给的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=150;
  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[550];
 10
 11 bool check(int n)   //保证有解
 12 {
 13     for(int i=1; i<=n; i++)    if( 1==(1&(ru[i]+chu[i])) )  return false;    //奇数个度
 14     for(int i=1; i<=n; i++)     //每个点可以补救。若不可改出度为100,入度为2,肯定不行。得补得上才行
 15     {
 16         if( can[i]-abs(notru[i]-notchu[i])>=0 )    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[4000], edg[550];
 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,0,sizeof(vis1));
 46     memset(vis2,0,sizeof(vis2));
 47     for(int i=0; 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, 1, 0, i);  //a的出度可给人
 54             add_node(b, a, 0, 0, i);
 55
 56             if(!vis1[a] && chu[a]>ru[a] )      //出度多,可流向别人
 57             {
 58                 sum_flow1+=(chu[a]-ru[a])/2;
 59                 vis1[a]=1;
 60                 add_node(0, a, (chu[a]-ru[a])/2, 0, -1);  //源点-(出边多的点)
 61                 add_node(a, 0, 0, 0, -1);
 62             }
 63             if(!vis2[b] && ru[b]>chu[b])    //所有缺边的都连到汇点
 64             {
 65                 sum_flow2+=(ru[b]-chu[b])/2;
 66                 vis2[b]=1;
 67                 add_node(b, n+1, (ru[b]-chu[b])/2, 0, -1 );
 68                 add_node(n+1, b, 0, 0, -1 );
 69             }
 70         }
 71     }
 72 }
 73
 74 int flow[N], path[N];
 75 int BFS(int s,int e)
 76 {
 77     deque<int> que(1,s);
 78     flow[s]=INF;
 79     while(!que.empty())
 80     {
 81         int x=que.front();
 82         que.pop_front();
 83         for(int i=0; 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=0;
101     while(true)
102     {
103         memset(flow,0,sizeof(flow));
104         memset(path,0,sizeof(path));
105         int tmp=BFS(s,e);
106         if(tmp==0)  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^1].flow-=tmp;
114             ed=edge[t].from;
115         }
116     }
117 }
118
119 void change_edge(int m)      //改变边的方向,重新建邻接表。
120 {
121     for(int i=0; i<edge_cnt; i+=2)
122         if(edge[i].has>=0 && edge[i].flow>0 )  //有流过的才需要改
123             swap(edg[edge[i].has].from , edg[edge[i].has].to );
124
125     for(int i=0; i<N; i++)    vec[i].clear();
126     for(int i=0; i<m; i++)    vec[edg[i].from].push_back(i);  //重新建立临接表
127 }
128
129 void fluery(int x)   //任意一个点开始即可
130 {
131     for(int i=0; i<vec[x].size(); i++)
132     {
133         int t=vec[x][i];
134         if(!vis[t]) //该边没遍历过
135         {
136             vis[t]=1;
137             fluery(edg[t].to);
138         }
139     }
140     ans.push_back(x);
141 }
142
143 void init()
144 {
145     edge_cnt=0;
146     sum_flow1=0;
147     sum_flow2=0;
148     memset(can, 0, sizeof(can));
149     memset(notru, 0, sizeof(notru));
150     memset(notchu, 0, sizeof(notchu));
151     memset(chu, 0, sizeof(chu));
152     memset(ru, 0, sizeof(ru));
153     memset(edge, 0, sizeof(edge));
154     memset(edg, 0, sizeof(edg));
155     for(int i=0; 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=0; 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‘?1:0);
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(0, n+1)!=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, 0, sizeof(vis));
197             ans.clear();
198             fluery(n);  //求欧拉回路路径
199             for(int i=ans.size()-1; i>0; i--)    printf("%d ",ans[i]); //反向输出路径
200             printf("%d\n",ans[0]);
201         }
202         if(t)   printf("\n");
203     }
204     return 0;
205 }

AC代码

时间: 2024-12-16 09:04:14

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

UVa10735 Euler Circuit (混合图的欧拉回路,最大流)

链接:http://vjudge.net/problem/UVA-10735 分析:题目保证底图联通,所以连通性就不用判断了.其次,不能把无向边转成有向边来做,因为本题中无向边只能经过一次,而拆成两条有向边之后变成了“沿着两个相反方向各经过一次”,所以本题不能拆边,而只能给边定向.首先我们给无向边任意定向,比如无向边(u,v),可以将其定向为u->v,于是u的出度和v的入度多1(在保证出入度相等的前提下,后者等价于出度少1),那我们这样随意定向以后反悔了怎么办呢?比如最后u的出度为4,入度为2,

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

链接: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1676 题意: 给出一个V个点和E条边(1≤V≤100,1≤E≤500)的混合图(即有的边是无向边,有的边是有向边),试求出它的一条欧拉回路,如果没有,输出无解信息.输入保证在忽略边的方向之后图是连通的. 分析: 很多混合图问题(例如,混合图的最短路)都可以转化为有向图问题,方法是把

UVA 10735 Euler Circuit

题意:求混合图的欧拉路径. 这题的困难之处在于无向边只能用一次,相当于一个方向未定的有向边. 首先用并查集判断图的连通性,(直接计数O(1),做1395 Slim Span学到的技巧). 我们知道有向图的欧拉路径存在的充要条件是最多两个点的入度不等于出度,而且相差为1,这题不需要考虑这种情况,只需要所有点的入度等于出度就行了. 对于无向边,一开始可以随意确定一个方向.这样不能保证所有点的入度等于出度,但是可以想办法调整. 比如说u->v,那么如果改变方向的话,就相当于把一个出度运输给了v. 这让

poj1637 Sightseeing tour,混合图的欧拉回路问题,最大流解

混合图的欧拉回路问题 题目地址 欧拉回路问题 1 定义 欧拉通路 (Euler tour)--通过图中每条边一次且仅一次,并且过每一顶点的通路. 欧拉回路 (Euler  circuit)--通过图中每条边一次且仅一次,并且过每一顶点的回路. 欧拉图--存在欧拉回路的图.  2 无向图是否具有欧拉通路或回路的判定  G有欧拉通路的充分必要条件为:G 连通,G中只有两个奇度顶点(它们分别是欧拉通路的两个端点). G有欧拉回路(G为欧拉图):G连通,G中均为偶度顶点.  3 有向图是否具有欧拉通路或

POJ 1637 Sightseeing tour(混合图的欧拉回路)

题目链接 建个图,套个模板. #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <algorithm> #include <vector> #include <string> #include <queue> using namespace std; #define INF 0x3ffffff str

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

传送门 这篇题解讲的真吼->这里 首先我们可以二分一个答案,然后把所有权值小于这个答案的都加入图中 那么问题就转化为一张混合图(既有有向边又有无向边)中是否存在欧拉回路 首先 无向图存在欧拉回路,当且仅当图的所有顶点度数都为偶数且图连通.        有向图存在欧拉回路,当且仅当图的所有顶点入度等于出度且图连通. 那么我们怎么判断混合图的欧拉回路是否存在呢? 我们把无向边的边随便定向,然后计算每一个点的入度和出度.如果有某一个点的入度和出度之差是奇数,那么肯定不存在欧拉回路. 因为欧拉回路要求

欧拉回路、混合图的欧拉回路

欧拉回路 从一个点开始把图中的边恰好经过一次,再回到出发点,这样的路径就是欧拉回路. 如图就是一个欧拉回路 欧拉回路判定 不过怎么样的图中才存在欧拉回路呢? 欧拉回路分有向图和无向图两种: 有向图: 图中所有点的入度等于出度 无向图: 图中所有点的度数都为偶数 这还是很好理解的,不过你可能要问,怎么知道一个混合图中有没有欧拉回路呢? 混合图的欧拉回路 过程: 先将无向边随意定向 判断每个点的入度和出度是不是同奇偶,不同则无解 用一个超级原点向出度小于入度的点连一条容量为 \({|入度-出度| \

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

题意: 给出一个图,有的边是有向边,有的是无向边.试找出一条欧拉回路. 分析: 按照往常的思维,遇到混合图,我们一般会把无向边拆成两条方向相反的有向边. 但是在这里却行不通了,因为拆成两条有向边的话,就表示这个边能“在两个相反方向各经过一次”. 而题意是这个边只能经过一次. 假设图中存在欧拉回路,则所有点的出度out(i) 等于 入度in(i) 不妨这样,先将所有的无向边任意定向,对于out(u) > in(u)的点,可以将已经定向的无向边u->v反向为v->u,这样out(u) - i

UVA 10735 混合图的欧拉回路

题意: 判断混合图中是否存在欧拉回路,如果存在欧拉回路,输出路径. 思路: 欧拉回路 存在每条边经过且只经过一次后回到原点的路径 在混合图中存在欧拉回路,需要满足以下条件: 1.把所有无向边任意规定一个方向后,对于每个点,满足 |入度-出度| % 2 == 0 2.按照上面已经规定的方向,利用 无向边 建图(有向边忽略掉),然后对于每个结点u,如果in[u]<out[u], 建边(s, u, |入度-出度| / 2);如果in[u]>out[u], 建边(u, t, |入度-出度| / 2)