歐拉回路基本概念+判斷+求解


歐拉回路基本概念+判斷+求解

1.定義

如果圖\(G\)(有向圖或者無向圖)中所有邊一次僅且一次行遍所有頂點的通路稱作歐拉通路。

如果圖\(G\)中所有邊一次僅且一次行遍所有頂點的回路稱作歐拉回路。

具有歐拉回路的圖成為歐拉圖(簡稱\(E\)圖)。具有歐拉通路但不具有歐拉回路的圖成為半歐拉圖。

頂點可以經過多次

畫個圖分辨一下:

  • 歐拉通路:
  • 歐拉回路:

簡單來講就是歐拉回路能夠回到起點

2.定理及推論

歐拉通路和歐拉回路的判定是很簡單的

無向圖\(G\)存在歐拉通路的充要條件是:

\(G\)為連通圖,並且\(G\)僅有兩個奇度節點(度數為奇數的頂點)或者無奇度節點

推論1:

  1. \(G\)是僅有兩個奇度節點的連通圖時,\(G\)的歐拉通路必以此兩個節點為端點
  2. \(G\)是無奇度節點的連通圖時,\(G\)必有歐拉回路
  3. \(G\)為歐拉圖(存在歐拉回路)的充分必要條件是\(G\)為無奇度節點的連通圖

有向圖\(D\)存在歐拉通路的充要條件是:

\(D\)為有向圖,\(D\)的基圖聯通,並且所有頂點的出度與入度都相等;或者除兩個頂點外,其余頂點的出度與入度都相等,而在這兩個頂點中一個頂點的出度與入度只差為\(1\),另一個頂點的出度與入度之差為\(-1\)

推論2:

  1. \(D\)除出、入度之差為\(1\)\(-1\)的兩個頂點之外,其余頂點的出度與入度都相等時,\(D\)的有向歐拉通路必以出、入度之差為\(1\)的頂點作為始點,以出、入度之差為\(-1\)的頂點作為終點
  2. \(D\)的所有頂點的出、入度都相等時,\(D\)中存在有向歐拉回路
  3. 有向圖\(D\)為有向歐拉圖的充分必要條件是\(D\)的基圖為連通圖,並且所有的頂點的出、入度都相等

3.歐拉通路回路存在的判斷

根據定理和推論,我們可以很好的找到歐拉通路回路的判斷方法

A.判斷歐拉通路是否存在的方法

  • 有向圖:圖連通,有一個頂點出度大於入度\(1\),有一個頂點入度大於出度\(1\),其余都是出度=入度
  • 無向圖:圖聯通,只有兩個頂點是奇數度,其余都是偶數度

B.判斷歐拉回路是否存在的方法

  • 有向圖:圖聯通,所有的頂點出度=入度
  • 無向圖:圖聯通,所有的頂點都是偶數度

4.歐拉回路的應用

  1. 哥尼斯堡七橋問題
  2. 一筆畫問題
  3. 旋轉鼓輪的設計

5.歐拉回路的判斷

\(DFS\)

鄰接矩陣的時間復雜度為\(O(n^2)\)

鄰接表的時間復雜度為\(O(n+e)\)

如果,重邊太多的話,鄰接表會掛掉:)

原題卡這個,所以我們要寫鄰接矩陣

const int N=1005;
int n,m;
int in[N];
bool vis[N],G[N][N];
void dfs(int x){
    vis[x]=1;
    for(int i=1;i<=n;i++){
        if(G[x][i]&&!vis[i]){
            dfs(i);
        }
    }
}
int main(){
    while(~scanf("%d",&n)&&n){
        m=read();
        memset(G,0,sizeof G);
        memset(vis,0,sizeof vis);
        memset(in,0,sizeof in);
        for(int i=1,x,y;i<=m;i++){
            x=read();y=read();
            G[x][y]=G[y][x]=1;
            in[x]++;in[y]++;
        }
        dfs(1);
        bool flag=1;
        for(int i=1;i<=n;i++){
            if(!vis[i]){
                flag=0;
                break;
            }
            if(in[i]%2){
                flag=0;
                break;
            }
        }
        if(flag)puts("1");
        else puts("0");
    }
}

並查集

const int N=1005;
int n,m;
int in[N],fa[N];
int find(int x){
    return x==fa[x]?x:x=find(fa[x]);
}
void union_set(int x,int y){
    x=find(x);y=find(y);
    if(x!=y)fa[x]=y;
}
int main(){
    while(~scanf("%d",&n)&&n){
        m=read();
        for(int i=1;i<=n;i++){
            fa[i]=i;
            in[i]=0;
        }
        for(int i=1,x,y;i<=m;i++){
            x=read();y=read();
            in[x]++,in[y]++;
            union_set(x,y);
        }
        bool flag=1;int totrt=0;
        for(int i=1;i<=n;i++){
            if(in[i]%2){
                flag=0;
                break;
            }
            if(find(i)==i){
                totrt++;
            }
        }
        //只有一個根且沒有入度為奇數的點
        if(flag&&totrt==1)puts("1");
        else puts("0");
    }
    return 0;
}

6.歐拉回路的求解

板子題:騎馬修柵欄

題目保證有解。

A.DFS搜索求解歐拉回路

基本思路:利用歐拉定理判斷出一個圖存在歐拉回路或歐拉通路后,選擇一個正確的起始頂點,用DFS算法遍歷所有的邊(每條邊只遍歷一次),遇到走不通就回退。在搜索前進方向上將遍歷過的邊按順序記錄下來。這組邊的排列就組成了一條歐拉通路或回路。

const int N=2005;
int m,Min,Max;
int G[N][N],in[N];
int path[N],cnt;
void dfs(int x){
    for(int i=Min;i<=Max;i++){
        if(G[x][i]){
            G[x][i]--;
            G[i][x]--;
            dfs(i);
        }
    }
    path[++cnt]=x;
}
void print_path(){
    for(int i=cnt;i>=1;i--){
        printf("%d\n",path[i]);
    }
}
int main(){
    m=read();Min=505,Max=0;
    for(int i=1,x,y;i<=m;i++){
        x=read();y=read();
        G[x][y]++;G[y][x]++;
        in[x]++,in[y]++;
        Max=max(Max,max(x,y));
        Min=min(Min,min(x,y));
    }
    for(int i=Min;i<=Max;i++){
        if(in[i]%2){
            dfs(i);
            print_path();
            return 0;
        }
    }//歐拉通路
    dfs(Min);//歐拉回路
    print_path();
    return 0;
}

B.Fleury(佛羅萊)算法

  • Fleury算法是對DFS爆搜的一種改進,使用DFS漫不經心的隨意走效率是不高的,Fleury是一種有效的算法

求法:

​ 設\(G\)為一無向歐拉圖,求\(G\)中一條歐拉回路的算法為:

  1. 任取\(G\)中一頂點\(v_0\),令\(P_0=v_0\)
  2. 假設沿\(P_i= v_0e_1v_1e_2...e_iv_i\)走到頂點\(v_i\),按下面方法從\(E(G)-{e_1,e_2,...,e_i}中選\)\(e_{i+1}\)
    1. \(e_{i+1}\)\(v_i\)相關聯
    2. 除法無別的邊可供選擇,否則\(e_{i+1}\)不應該是\(G_i=G-{e_1,e_2,...,e_i}\)中的橋
  3. 當2.無法進行時算法停止

可以證明的是,當算法停止時,所得到的簡單回路\(P_m= v_0e_1v_1e_2...e_mv_m\)\(G\)中一條歐拉回路

我個人感覺是把大連通塊分成了零散的幾個小連通塊然后分塊連接(?)

關鍵是能不走橋就不走橋,實在無路可走了才會去走橋

const int N=2005;
int n,m;
int top,sta[N];
bool G[N][N];
void dfs(int x){
    sta[++top]=x;
    for(int i=1;i<=n;i++){
        if(G[x][i]>0){
            G[x][i]=G[i][x]=0;
            dfs(i);
            break;
        }
    }
}
void Euler(int x){
    bool brige;
    top=1;
    sta[top]=x;
    while(top>=0){
        brige=0;
        for(int i=1;i<=n;i++){
            if(G[sta[top]][i]>0){
                brige=1;
                break;
            }
        }
        if(!brige){
            printf("%d ",sta[top--]);
        }
        else {
            top--;
            dfs(sta[top+1]);
        }
    }
}
int main(){
    n=read();m=read();
    for(int i=1,x,y;i<=m;i++){
        x=read();y=read();
        G[x][y]=1;
        G[y][x]=1;
    }
    int num=0,start=1;
    for(int i=1;i<=n;i++){
        int deg=0;
        for(int j=1;j<=n;j++)
            deg+=G[i][j];
        if(deg%2){
            start=i;
            num++;
        }
    }   
    if(num==0||num==2)Euler(start);
    else {
        puts("No Euler path");
    }
    return 0;
}

To be continue……


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM