欧拉图
欧拉图
定义:
-
欧拉回路:若存在一条从起点S出发的路径,每条边恰好只走一次,最终回到起点S的路径被称为欧拉路径。
-
欧拉通路:若存在一条从起点S出发的路径,经过每条边一次,但是不要求回到起点S的路径被称作欧拉通路。
每条路都走,而且只走一次,并且能回到起点的,是欧拉图,也称欧拉回路。
每条路都走,而且只走一次,不能回到起点的,是半欧拉图,也称欧拉路径。
判定方法:
前提:图都是连通图
无向图:
当一个无向图是欧拉图时,它的所有顶点的度数都是偶数,即它没有奇度顶点。
当一个无向图是半欧拉图时,它有且只有2个奇度顶点
有向图:
当一个有向图是欧拉图时,它的所有顶点都处于一个强连通分量中,并且出度=入度。
强连通分量:强连通分量指强连通分量中的任意两个点都有路径互相到达。
当一个有向图是半欧拉图时,它的所有顶点都处于一个强连通分量中,且:
最多只有一个顶点的出度与入度差为 1 。
最多只有一个顶点的入度与出度差为 1 。
所有其他顶点的入度和出度相同。
Hierholzier 算法(蓝书P409)时间复杂度O(n*m)
(1)判断奇度数点,奇度数点若为 0 0 0则任意指定起点,奇度数点为 2 2 2则从指定其中任意一个。
(2)对于当前节点 x x x,扫描与 x x x相连的所有边,当扫描到一条边 ( x , y ) (x,y) (x,y)时,删除该边以及边 ( y , x ) (y,x) (y,x),并递归 y y y。扫描完所有边后,将 x x x加入答案队列。
(3)倒序输出答案队列。
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=1e5+5;
const int maxm=1e5+5;
struct edge
{
int to,nxt;
}Edge[maxm<<1];
int head[maxn],Stack[maxm<<1],ans[maxm<<1];
bool vis[maxn<<1];
int n,m,tot=1,top,num;//tot一定要从1开始
void addedge(int x,int y)
{
Edge[++tot].to=y,Edge[tot].nxt=head[x],head[x]=tot;
}
void euler()
{
top=0;
Stack[++top]=1;//用数组模拟栈
int x,i;
while(top>0)//模拟dfs
{
x=Stack[top],i=head[x];
while(i&&vis[i])
i=Edge[i].nxt;//找一条没有走过的
if(i)
{
Stack[++top]=Edge[i].to;
vis[i]=vis[i^1]=1;//有向图则只标记一条边
head[x]=Edge[i].nxt;//删去已经走过的边,仔细体会删去的含义
}
else//x已经访问完毕 回溯过程
{
--top;
ans[++num]=x;
}
}
}
int main()
{
scanf("%d%d",&n,&m);
int x,y;
for(int i=0;i<m;i++)
{
scanf("%d%d",&x,&y);
addedge(x,y),addedge(y,x);
}
euler();
for(int i=num;i>=1;i--)//倒序输出
printf("%d\n",ans[i]);
return 0;
}
如果我们每次都贪心取编号最小的顶点,那么得到的欧拉迹是所有欧拉迹中编号字典序最小的(提示:可以用multiset)
例题:无序字母对
解法:将字母对的关系看成点与点的关系,中间用一条边相连,字符串即它们的欧拉回路。