一、前置技能
> 图的概念
> dfs遍历图
> stack
> 一笔画
二、定义
看过上面链接 是不是对一笔画产生了兴趣呢?
(A:没有!)
好吧……其实通俗来讲 欧拉路径就是OI意义下的一笔画
也就是 图中经过每条边一次并且仅一次的路径。
欧拉回路就是满足欧拉路径性质的回路
同时我们定义欧拉图(其实就是一笔画出来的图)
存在欧拉回路的图称为欧拉图。
存在欧拉路径但不存在欧拉回路的图称为半欧拉图。
三、性质与定理
这种路径很特殊 不一定所有的图都有(比如大部分树)
规定一个点的度数为过这个点的边数(应该都知道)
在刨去孤立点(没有度数的点)之后(显然,这样做并不会影响图中欧拉回路的存在性。)
> Q:先考虑无向图
发现我们考虑边无法推知下一步的方向 转而考虑点
然后就可以发现 在欧拉路径中除了起点和终点 进入每个点一次的时候一定会出去一次
所以除了起点和终点每个点的度数均为偶数
而欧拉回路中起点和终点重合
所以 欧拉图满足 每个点的度数为偶数
半欧拉图满足 仅有两个点的度数为奇数
那么 每个点度数为偶数的图(>0)一定有欧拉回路吗?
证明:
首先 如果图中没有回路 那么一定是一颗树 就一定有度数不是偶数的点(叶子)
所以一定有回路
然后比如我们找到了一个回路C
当我们把这条回路上的边都不计的时候
由于这是一条回路每一个点的度数减少量也一定是偶数(随手画个环看一下?)
剩下的图中就一定还有回路 把两个回路拼起来就有新的回路
最后全拼起来就能遍历所有的边 于是C就变成了欧拉回路
(欧拉路证明类似)
> Q:那么有向图?
同理 每条边有起点和终点 就把度数变成 入度 和 出度
当入度==出度 也就是进入的边数和出去的边数一样的时候 满足是欧拉图
仅有一个入度为1 仅有一个出度为1的时候 满足是半欧拉图
> Q:那么混合图?
(啪!)
混合图欧拉回路是一个经典网络流问题 放别的地方讲
> Q:所以……怎么实现?
其实刚刚证明的时候就已经说完了最常用的 套圈法 的基本过程
实现的时候开一个栈存删掉的边(是否惰性删除都行)
最后沿着栈逆序输出就OK了
注意欧拉回路没有一定的起点 所以经常要输出字典序最小
这个时候邻接矩阵就比较方便 当然像某些dalao把链前排序也行
这里粘一下Code吧
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cmath> 5 #define itn int 6 #define ms(a,b) memset(a,b,sizeof a) 7 #define rep(i,a,n) for(int i = a;i <= n;i++) 8 #define per(i,n,a) for(int i = n;i >= a;i--) 9 using namespace std; 10 typedef long long ll; 11 ll read() { 12 ll as = 0,fu = 1; 13 char c = getchar(); 14 while(c < '0' || c > '9') { 15 if(c == '-') fu = -1; 16 c = getchar(); 17 } 18 while(c >= '0' && c <= '9') { 19 as = as * 10 + c - '0'; 20 c = getchar(); 21 } 22 return as * fu; 23 } 24 const int N = 550; 25 //head 26 int n,m; 27 int a[N][N]; 28 int stk[N<<1],top; 29 void dfs(int x) { 30 rep(y,1,n) { 31 if(!a[x][y]) continue; 32 a[x][y]--,a[y][x]--; 33 dfs(y); 34 } 35 stk[++top] = x; 36 } 37 int du[N]; 38 int main() { 39 m = read(); 40 rep(i,1,m) { 41 int x = read(),y = read(); 42 n = max(max(x,y),n); 43 a[x][y]++,a[y][x]++; 44 du[x]++,du[y]++; 45 } 46 bool flag = 1; 47 rep(i,1,n) 48 if(du[i] & 1) { 49 if(flag) dfs(i); 50 flag = 0; 51 } 52 if(flag) dfs(1); 53 while(top) printf("%d ",stk[top--]); 54 return 0; 55 }
应该是luogu一道绿题吧……