俗称一笔画问题。
给定一张无向图,若存在一条从节点 S S S到节点 T T T的路径,恰好不重不漏的经过每条边一次(可以重复经过节点),则称该路径为 S S S到 T T T的欧拉路。特别地,若存在一条从节点 S S S出发的路径,恰好不重不漏的经过每条边一次(可以重复经过节点),最终回到起点 S S S,则称该路径为欧拉回路。存在欧拉回路的无向图被称为欧拉图。
一张无向图为欧拉图,当且仅当无向图连通,并且每个点的度数都是偶数。
一张有向图为欧拉图,当且仅当所有顶点的入度等于出度且该图是连通图。
一张无向图中存在欧拉路,当且仅当无向图连通,并且图中恰好有两个节点的度数为奇数,其他节点的度数都是偶数。这两个度数为奇数的节点就是欧拉路的起点 S S S和终点 T T T。
一张有向图中存在欧拉路,当且仅当无向图连通,并且图中恰好有两个节点满足:它们的入度分别比它们的出度大 1 1 1和小 1 1 1,出度比入度大的那个节点就是欧拉路的起点 S S S,另外一个节点就是欧拉路的终点 T T T。
欧拉图中每个节点度数为偶数说明:只要到达一个节点,就必定有一条尚未走过的边可以离开该点。
(1)任取 v 0 ∈ V ( G ) v_{0}∈V(G) v0∈V(G),令 P 0 = v 0 P_{0}=v_{0} P0=v0;
(2)设 p i = v 0 e 1 v 1 e 2 . . . e i v i p_{i}=v_{0}e_{1}v_{1}e_{2}...e_{i}v_{i} pi=v0e1v1e2...eivi已经走遍,然后从 E ( G ) − ( e 1 , e 2 , e 3 , . . . , e i ) E(G)-(e_{1},e_{2},e_{3},...,e_{i}) E(G)−(e1,e2,e3,...,ei)中选取 e i + 1 e_{i+1} ei+1,选取规则如下:
1、 e i + 1 e_{i+1} ei+1与 v i v_{i} vi相关联;
2、除非没有别的边可以走,否则 e i + 1 e_{i+1} ei+1不应该为 G i = G − ( e 1 , e 2 , e 3 , . . . , e i ) G_{i} = G-(e_{1},e_{2},e_{3},...,e_{i}) Gi=G−(e1,e2,e3,...,ei)中的桥
(3)当(2)不能在进行时,算法停止。
找欧拉回路,或者欧拉路径。
以无向图为例:
(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)倒序输出答案队列。
c o d e : code: code:
#include
#include
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;
}
注意处理有向图时 s t a c k stack stack和 a n s ans ans的空间要开的大一点,不能只开 O ( n ) O(n) O(n)的空间,开比边数稍大一点的空间比较好。
首先给原图中的每条无向边随便指定一个方向(称为初始定向),将原图改为有向图 G ′ G' G′,然后任务就是改变 G ′ G' G′中某些边的方向(当然是无向边转化来的,原混合图中的有向边不能动)使其满足每个点的入度等于出度。
设 D [ i ] D[i] D[i]为 G ′ G' G′中(点 i i i的出度 − - − 点 i i i的入度)。可以发现,在改变 G ′ G' G′中边的方向的过程中,任何点的 D D D值的奇偶性都不会发生改变(设将边 < i , j > <i,j>改为 < j , i >
若初始 D D D值都是偶数,则将 G ′ G' G′改装成网络:设立源点 S S S和汇点 T T T,对于每个 D [ i ] > 0 D[i]>0 D[i]>0的点 i i i,连边 < S , i > <S,i>,容量为 D [ i ] / 2 D[i]/2 D[i]/2;对于每个 D [ j ] < 0 D[j]<0 D[j]<0的点 j j j,连边 < j , T >
为什么能这样建图?
考虑网络中的一条增广路径 S − > i − > . . . − > j − > T S->i->...->j->T S−>i−>...−>j−>T,将这条从 i i i到 j j j的路径在 G ′ G' G′中全部反向,则: i i i的入度加 1 1 1出度减 1 1 1, j j j的入度减 1 1 1出度加 1 1 1,路径中其它点的入度出度均不变。而 i i i是和 S S S相连的,因此初始 D [ i ] > 0 D[i]>0 D[i]>0,即 i i i的出度大于入度,故这样反向之后 D [ i ] D[i] D[i]减少 2 2 2;同理, j j j是和 T T T相连的,这样反向之后 D [ j ] D[j] D[j]增加 2 2 2。因此,若最大流中边 < S , i > <S,i>满流(流量为初始 D [ i ] / 2 D[i]/2 D[i]/2),此时 D [ i ] D[i] D[i]值就变成了 0 0 0,也就是 i i i的入度等于出度。因此只要使所有 S S S引出的边全部满流,所有初始 D D D值 > 0 >0 >0的点的 D D D值将等于 0 0 0,又因为将边变向后所有点的 D D D值之和不变,所有初始 D D D值小于 0 0 0的点的 D D D值也将等于 0 0 0,而初始 D D D值等于 0 0 0的 D D D点既不与 S S S相连也不与 T T T相连,所以它们是网络中的中间点,而中间点的流入量等于流出量,故它们的入度和出度一直不变,即 D D D值一直为 0 0 0。因此,整个图 G ′ G' G′成为欧拉图。
前面已经说过,在将边变向的过程中任何点的 D D D值的奇偶性都不会改变,而一个有向图有欧拉路径的充要条件是基图连通且有且只有一个点的入度比出度少 1 1 1(作为欧拉路径的起点),有且只有一个点的入度比出度多 1 1 1(作为终点),其余点的入度等于出度。这就说明,先把图中的无向边随便定向,然后求每个点的 D D D值,若有且只有两个点的初始 D D D值为奇数,其余的点初始 D D D值都为偶数,则有可能存在欧拉路径(否则不可能存在)。进一步,检查这两个初始 D D D值为奇数的点,设为点 i i i和点 j j j,若有 D [ i ] > 0 且 D [ j ] < 0 D[i]>0且D[j]<0 D[i]>0且D[j]<0,则 i i i作起点 j j j作终点(否则若 D [ i ] D[i] D[i]与 D [ j ] D[j] D[j]同号则不存在欧拉路径),连边 < j , i >