欧拉回路

定义

欧拉回路:图G中经过每一条一次边并且仅一次的回路称作欧拉回路。 欧拉路径:图G中经过每一条边一次并且仅一次的路径叫做欧拉路径。 欧拉图:存在欧拉回路的图叫欧拉图 半欧拉图:存在欧拉路径但不存在欧拉回路的图叫半欧拉图。

判断

定理1:无向图 G为欧拉图,当且仅当G为连通图且所有定点的度数为偶数 推论1(小学奥数):无向图G为欧拉图,当且仅当有2个顶点的度数为奇数,其他所有均为偶数时。 定理2:有向图 G为欧拉图,当且仅当G的基图联通,且所有顶点的入度等于出度。(基图:忽略有向图所有边的方向,得到的无向图成为该有向图的基图)。 推论二:有向图为半欧拉图,当且仅当G的基图联通,并且存在顶点u的入度比出度大1,v入度比出度小1,其他所有点入度等于出度。 性质一:当C是图中一个简单回路,此回路删去,留下的图的各极大联通子图都存在一条欧拉回路。(显然,用点将各联通子图连接,才能形成欧拉回路。) 性质二:当C1,C2是两个简单回路,无边相交,仅有一点公共,可以将其合并成另一个简单回路。

操作步骤

1.在图G中找到一个回路C; 2。将图G 中属于回路C的边删除 3.在残留图的各极大连同子图中分别寻找欧拉回路 4.将各极大连同子图的欧拉回路合并到C中,得到图G的欧拉回路; 其实没有说的那么玄乎,其实就是从一个点u出发,遍历到达的每个点,然后把到达的每个点v的路径删掉,然后继续搜索(魔改版tarjan?)最后把u->v的边加入栈S。At last,依次取出栈s中的各元素,得到原图G的欧拉回路。复杂度$O(|E|)$

一些小技巧

1.你可以把每一条边遍历以后,在钱向星里面把head[x]改成edges[i].next以达到删边的效果。 2.代码里还有把循环里面的int i改成int &i的小优化,但是注意这样以后在循环中使用的时候要复制一下,因为你push_stack的时候传入的是引用,在stack里面可能会对i进行操作,导致值改变,然后就出锅了。

例题

uoj117 代码:


//
// Created by dhy on 18-11-24.
//
# pragma GCC optimize("Ofast")
# include <stack>
# include <cstdio>
# include <set>
using namespace std;
const int MAXN = (int)1e5+10;
const int MAXM = (int)2e5+10;
struct edge{
    int t,w,next;
}edges[MAXM<<1];
int head[MAXN];
int top = 0;
int type;
void add(int f,int t,int w) {
    edges[++top].next = head[f];
    edges[top].t = t;
    edges[top].w = w;
    head[f] = top;
}
int read(){
    int x = 0,f = 1;
    static char c = getchar();
    while(c<'0'||c>'9'){ if(c=='-')f = -1;c = getchar(); }
    while(c>='0'&&c<='9'){ x = (x<<1)+(x<<3)+c-'0';c = getchar(); }
    return x*f;
}
stack<int> stk;
bool vis[MAXN<<1];
void dfs(int u){//undirected graph
    for(int &j = head[u];j;j = edges[j].next){
        int i = j;
        if(!vis[(i+1)>>1]){
            vis[(i+1)>>1] = true;
            dfs(edges[i].t);
            stk.push((i&1)?((i+1)>>1):-((i+1)>>1));
        }
    }
}
void dfs2(int u){//directed graph
    for(int &i = head[u];i;i = edges[i].next){
        int j = i;
        if(!vis[j]){
            vis[j] = true;
            dfs2(edges[j].t);
            stk.push(j);
        }
    }

}
int in[MAXN],out[MAXN];
int main(void){
    int n,m;
    type = read();
    n = read(),m = read();
    int u;
    int f,t,w;
    for(int i = 1;i<=m;i++){
        f = read(),t = read();
        u = f;
        add(f,t,1);
        out[f]++;
        in[t]++;
        if(type==1)add(t,f,1);
    }
    if(type==1){//对于无向图
        for(int i = 1;i<=n;i++){
            if(!(in[i]+out[i]&1)==0){//无向图度之和为偶数
                printf("NO");
                return 0;
            }
        }
        dfs(u);
        if(stk.size()!=m){//找到的答案小于边数,说明图根本不连通
            printf("NO");
            return 0;
        }
        printf("YES\n");
        while(!stk.empty()){
            printf("%d ",stk.top());
            stk.pop();
        }
    }else{//对于无向图
        for(int i = 1;i<=n;i++){
            if(in[i]!=out[i]){//入度等于出度
                printf("NO");
                return 0;
            }
        }
        dfs2(u);
        if(stk.size()!=m){
            printf("NO");
            return 0;
        }
        printf("YES\n");
        while(!stk.empty()){
            printf("%d ",stk.top());
            stk.pop();
        }
    }

    return 0;
}