欧拉回路


欧拉路径&欧拉回路

判定

无向图

  1. 欧拉路径:可以一笔画,充要条件是度数为奇数的点的个数为0或2
  2. 欧拉回路:欧拉路劲构成一个环,充要条件是全部是偶点

有向图

  1. 欧拉路径:起点出度比入度大1,终点入度比出度大1,其他全部是偶点
  2. 欧拉回路:每个点出入度相同(至多存在两个点出入度不同,且这两个点满足起点出度比入度大1,终点入度比出度大1),且存在一系列环可以覆盖原图

求路径:Fleury算法

求图的欧拉回路的算法:Fleury算法

对于无向图有向图,求路径的方法是一样的,简单描述为:在任意可行点处进行dfs,过程中找到一条回路之后从终点开始往回删除dfs过程中的所有边(注意,这里如果是无向图的话,需要删双向边,那么可以通过异或操作完成)如果删掉这些边之后的点还能够通往别的路径的话,就继续dfs通往,直到递归结束,此时,倒着输出所有遍历的节点就行了(无向图正着倒着都一样),以上过程都可以通过dfs的特性(栈)一气呵成

伪代码

1
2
3
4
5
6
7
8
9
void dfs(int u){
for(遍历所有u的出边){
if(出边没被删){
删掉这条边(无向图需要删双向,可以用^运算实现)
dfs(这条边所连的点)
}
}
把u压入栈中
}

具体代码见下方例题2、3

模板题

1. HihoCoder 1176

判断一个无向图是否有欧拉路径

code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include<bits/stdc++.h>
using namespace std;
int n,m;
vector<int> gg[50005];
int main(){
cin>>n>>m;
int u,v;
for(int i=1;i<=m;i++){
cin>>u>>v;
gg[u].push_back(v);
gg[v].push_back(u);
}
int ans=0;
for(int i=1;i<=n;i++){
int temp=0;
int Size=gg[i].size();
for(int i=0;i<Size;i++){
temp++;
}
if(temp%2!=0){
ans++;
}
}
if(ans==0||ans==2) cout<<"Full"<<endl;
else cout<<"Part"<<endl;
return 0;
}

2. HihoCoder 1181

重题POJ 2230 Watchcow(但此题用stack会RE)

求一个无向图的任意一条欧拉回路

应用Fleury算法,代码如下

code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include<bits/stdc++.h>
using namespace std;
int n,m;
struct Edge{
int u,v,w,next;
bool f;
}e[50005];
int cnt,head[50005];
void init(){
memset(head,-1,sizeof(head));
cnt=0;
}
inline void addedge(int u,int v,int w=0){
e[cnt]=Edge{u,v,w,head[u],false};
head[u]=cnt++;
}
stack<int> sta;
void dfs(int u){
for(int i=head[u];~i;i=e[i].next){
int v=e[i].v;
if(!e[i].f){
e[i].f=e[i^1].f=1;
dfs(v);
}
}
sta.push(u);
}
int main(){
cin>>n>>m;
int u,v;init();
for(int i=1;i<=m;i++){
cin>>u>>v;
addedge(u,v);
addedge(v,u);
}

dfs(1);
while(sta.size()){
cout<<sta.top()<<" ";
sta.pop();
}
return 0;
}

3. Hiho Coder 1182

题意:

给出一个正整数n,求一个长度为$2^n$的01环,使得该环的每n位恰好构成$0~2^{n-1}$,例如:n=3时,答案为00010111

题解:

很有意思的一道题,考虑将其转化为图论题:例如,对于n=3的情况,抽取其中任意的长度为3的子串,如:101,将其拆分为10、01,那么就可以建图从(10)->(01),由此,从$0~2^{n-1}-1$只要有一个数的后n-1位和另一个数的前n-1位相同,那么就可以从前一个数连一条边到后一个数,显然共有$2^n$条边,跑一次欧拉回路,用栈记录路径,最后输出时一定要倒着输出(栈&1)的值

AC代码:

code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include<bits/stdc++.h>
using namespace std;
int n,m;
struct Edge{
int u,v,w,next;
bool f;
}e[50005];
int cnt,head[50005];
void init(){
memset(head,-1,sizeof(head));
cnt=0;
}
inline void addedge(int u,int v,int w=0){
e[cnt]=Edge{u,v,w,head[u],false};
head[u]=cnt++;
}
stack<int> sta;
void dfs(int u){
for(int i=head[u];~i;i=e[i].next){
int v=e[i].v;
if(!e[i].f){
e[i].f=1; //删单边
dfs(v);
}
}
sta.push(u);
}
int main(){
cin>>n;
if(n==1){
cout<<"01"<<endl;
return 0;
}
init();
int k=(1<<n-2)-1;
for(int i=0;i<1<<n-1;i++){
int giao=i&k;
for(int j=0;j<1<<n-1;j++){
if(giao==j>>1){
addedge(i,j);
}
}
}

dfs(0);
sta.pop();
while(sta.size()){
cout<<(sta.top()&1);
sta.pop();
}
return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!