求欧拉路的板子

11490DX Re: Master Lv.15

 

定义

首先,什么是欧拉路?

生活中最熟悉的欧拉路(也是欧拉路径)应该就是小时候玩的“一笔画”游戏,要求从某个点出发,某个点停止,经过图中的每条边仅一次到达图中的一个点。

而欧拉路的定义与此相似:从图中某个图出发,遍历整个图,经过每条边有且仅有一次

而欧拉回路就是从这个点出发,最终回到这个点。

欧拉路问题是 DFS 的一个经典应用。

判断是否存在欧拉路

要遍历完整个图,经过每一条边,就相当于除了起点和终点,其他的点你进去了就肯定要出来。

不管其他点经过多少次,你会发现:不是起点或终点的点的入度和出度都相等

否则你进了这个点出不来,就遍历不完整个图。

而你是从起点出来,最终回到终点。那就有两种情况:

  • 若起点和终点是同一个点:那么图上所有的点入度和出度都相等。因为你从这个点出去,最后回到这个点,就是出度 ,入度 ,最后入度还是等于出度。
  • 若起点和终点不是同一个点:那么起点的出度比入度大1,终点的入度比出度大1。因为你从起点出来,起点入度 ,最后回到终点,终点入度

那么如果他有一个起点和一个终点(或是没有起点终点),并且其他的点入度等于出度,那么他就有欧拉路。否则就没有。

并且还要判断它是否连通!如果不连通那就没有欧拉路。

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
//判断一个连通有向图有无欧拉回路
for(int i=1;i<=m;i++){
cin>>u>>v;// u->v
//indeg[v]++, outdeg[u] ++;
deg[v] ++, deg[u] --;
}
int stt=0, edd=0;
for(int i=1;i<=n;i++){
if(deg[i] == -1){
if(stt){
cout<<"No\n";
return 0;
}
stt = i;
}else if(deg[i] == 1){
if(edd){
cout<<"No\n";
return 0;
}
edd = i;
}else if(deg[i] != 0){
cout<<"No\n";
return 0;
}
}
if(stt == 0 && edd == 0){
cout<<"Yes\n";
}else if(stt == 0 || edd == 0){
cout<<"No\n";
}else cout<<"Yes\n";

输出一条欧拉路

根据上面的推断就可以确定这个欧拉路的起点和终点了。如果它是欧拉回路,那就随便选一个点作为起点。

从这个点开始 DFS,经过一条边就给这条边打标记,然后从这个点发散出的所有边搜索完了之后就输出这个点。

这相当于就是 DFS 回溯。而回溯的顺序正好是欧拉回路的逆序。

img
(图为给欧拉图 DFS 的一种顺序和其回溯的顺序)

于是就可以愉快地写代码了:

1
2
3
4
5
6
7
8
9
10
int st[N], len; //一只栈
void dfs(int x){
for(int i=h[x];i;i=edge[i].nxt){
if(!vis[i]){ // 若这条边没有被访问
vis[i] = 1; // 打上标记
dfs(edge[i].to); // 然后给这条边的终点 DFS
}
}
st[++len] = x; // 回溯顺序的逆序就是欧拉路径的顺序
}

模板题:P7771 欧拉路径

这道题和上面讲的不一样的是:这道题要求我们输出字典序最小的欧拉路径

字典序最小是好办的,就让这个点能够到达的边编号最小的先 DFS,然后编号次小的再 DFS……这样就解决了字典序最小的问题。

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
53
54
55
56
57
58
59
60
61
62
#include<bits/stdc++.h>
using namespace std;

const int N = 1e5+5;
vector<int> edge[N];
int n, m;
int st[N<<1], len;
int sttvis[N];//Where can i start?
void dfs(int x){
int siz = edge[x].size();
for(int i=sttvis[x];i<siz;i=sttvis[x]){
sttvis[x] = i+1;
dfs(edge[x][i]);
}
st[++len] = x;
}
int deg[N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin>>n>>m;
int u, v;
for(int i=1;i<=m;i++){
cin>>u>>v;
edge[u].push_back(v);
//indeg[v]++, outdeg[u] ++;
deg[v] ++, deg[u] --;
}
int stt=0, edd=0;
for(int i=1;i<=n;i++){
if(deg[i] == -1){
if(stt){
cout<<"No\n";
return 0;
}
stt = i;
}else if(deg[i] == 1){
if(edd){
cout<<"No\n";
return 0;
}
edd = i;
}else if(deg[i] != 0){
cout<<"No\n";
return 0;
}
}
if(stt == 0 && edd == 0){
stt = edd = 1;
}else if(stt == 0 || edd == 0){
cout<<"No\n";
return 0;
}
for(int i=1;i<=n;i++){
sort(edge[i].begin(), edge[i].end());
}
dfs(stt);
for(int i=len;i>=1;i--){
cout<<st[i]<<' ';
}
return 0;
}
  • Title: 求欧拉路的板子
  • Author: 11490DX
  • Created at : 2024-11-05 15:13:47
  • Updated at : 2025-02-14 13:05:05
  • Link: https://11490dx.net/2024/11/05/euler-path-stt/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments