欧拉路径的定义:
如果图G中的一个路径包括每个边恰好一次,则该路径称为欧拉路径(Euler path)。
如果一个回路是欧拉路径,则称为欧拉回路(Euler circuit)。
具有欧拉回路的图称为欧拉图(简称E图)。具有欧拉路径但不具有欧拉回路的图称为半欧拉图。
欧拉路径以及欧拉回路的判断:
奇点的定义:指跟这个点相连的边数目有奇数个的点。
定理1:存在欧拉路的条件:图是连通的,有且只有2个奇点。
定理2:存在欧拉回路的条件:图是连通的,有0个奇点。
为了更形象的了解这两个定理,请看下图:
这是一条欧拉路径,有且只有2个奇点,这里1和4是奇点,因为只有一条边和他们相连
这是一条欧拉回路,从起点开始最后还能回到起点,没有奇点,因为1,2,3,4没有一个点是奇数条边与它相连
欧拉路和欧拉回路的题目往往都会与深度优先搜索(DFS)关联,一笔画问题是欧拉路中的一道典型例题
一笔画问题
Description
如果一个图存在一笔画,则一笔画的路径叫做欧拉路,如果最后又回到起点,那这个路径叫做欧拉回路。
我们定义奇点是指跟这个点相连的边数目有奇数个的点。对于能够一笔画的图,我们有以下两个定理。
定理1:存在欧拉路的条件:图是连通的,有且只有2个奇点。
定理2:存在欧拉回路的条件:图是连通的,有0个奇点。
两个定理的正确性是显而易见的,既然每条边都要经过一次,那么对于欧拉路,除了起点和终点外,每个点如果进入了一次,显然一定要出去一次,显然是偶点。对于欧拉回路,每个点进入和出去次数一定都是相等的,显然没有奇点。
求欧拉路的算法很简单,使用深度优先遍历即可。
根据一笔画的两个定理,如果寻找欧拉回路,对任意一个点执行深度优先遍历;找欧拉路,则对一个奇点执行DFS,时间复杂度为O(m+n),m为边数,n是点数。
Input
第一行n,m,有n个点,m条边,以下m行描述每条边连接的两点。保证是一个连通的简单图,即两点之间没有多重边。1<=n, m<=1000。
Output
输出一个路径,按照字典序的最小序输出,用一个空格隔开,构成欧拉通路或欧拉回路。数据保证一定有通路或回路。
Sample Input 1
5 5
1 2
2 3
3 4
4 5
5 1
Sample Output 1
1 2 3 4 5 1
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e4+5;
int mp[maxn][maxn];
int d[maxn]; //记录每个点的度
int n,m;
vector<int>path;
void dfs(int x) //深搜找路径
{
for(int i = 1 ; i <= n ; i++)
{
if(mp[x][i])
{
mp[x][i] = mp[i][x] = 0;
dfs(i);
}
}
path.push_back(x);
}
int main()
{
cin >> n >> m;
for(int i = 1 ; i <= m ; i++)
{
int x,y;
cin >> x >> y;
mp[x][y] = mp[y][x] = 1; //把可以连通的点标记为1
d[x]++;
d[y]++;
}
int st = 1;
for(int i = 1 ; i <= n ; i++)
{
if(d[i] % 2 != 0) //因为题目已经说过一定存在欧拉路或者欧拉回路所以只要找到一个奇点即可
{
st = i;
break;
}
}
dfs(st);
int len = path.size();
cout << path[len - 1];
for(int i = len - 2 ; i >= 0 ; i--)//倒叙输出路径,因为在DFS中存路径实际上是一个递归的过程
{
cout << " " << path[i] ;
}
return 0;
}