1. 程式人生 > >歐拉回路 && 歐拉路徑

歐拉回路 && 歐拉路徑

main aps lin 技術 c++ edge AD rac ans

歐拉路徑()定義 : 如果有一條路徑使得能夠走完所有的邊且每一條邊經過有且只有一次,這樣的路徑叫做歐拉路徑

歐拉回路定義 : 如果有從起點出發最後回到起點的一條路徑使得能夠走完所有的邊且每條邊經過有且只有一次,稱其為歐拉回路

根據定義,歐拉回路是歐拉路徑的一個子集,存在歐拉回路定存歐拉路徑,反之則不一定成立

半歐拉圖 : 有歐拉路徑而沒有歐拉回路的圖

歐拉圖 : 包含至少一個歐拉回路的圖

如何判斷一幅圖是否有歐拉路徑/回路? PS : 以下的歐拉路徑條件都是針對半歐拉圖來說

① 有向圖情況

歐拉路徑 ==> 有且只有一個點的出度 - 入度 == 1、有且只有一個點的入度 - 出度 == 1、其余點的出入度相等

歐拉回路 ==> 所有點的出度 == 入度

② 無向圖情況

歐拉路徑 ==> 有且只有兩個點的度為奇數(起點、終點)、其他點的度為偶數

歐拉回路 ==> 所有點的度都為偶數

以上只能判定路徑的有無、而要找到其中一條歐拉路徑/回路,有兩種算法

一種是 Fluery 算法、一種是 Hierhoizers 算法

Fluery 算法 : 略......、給個鏈接

Hierhoizers 算法 :

此算法是基於 DFS 的路徑回復算法,前提條件是給其指定好起點

算法會自動尋找歐拉回路、找不到的情況下會找到歐拉路徑

/*
開始DFS遞歸函數(當前頂點 x):
    尋找與 x 相連的邊(x,v):
        刪除 (x,v)
        刪除 (v,x)///如果是無向圖的話
        DFS(v)
    將x插入到路徑棧中
*/ struct EDGE{ int v, nxt; bool used; }; ///鏈式向前星邊結構體定義 stack<int> path; ///定義棧記錄頂點路徑 void DFS(int x) { for(int i=Head[x]; i!=-1; i=Edge[i].nxt){ int Eiv = Edge[i].v; if(!Edge[i].used){ Edge[i].used = true; DFS(x); } } path.push(x); }

例題 POJ 2337 Catenyms

以 26 個字母為頂點、以給出的單詞為邊,找出一條字典序最小的歐拉路徑就是答案

由於這裏要字典序最小,基於我們尋找歐拉路徑的DFS算法,所以在進行建圖的時候

最好將單詞進行字典序排序再來插入到鄰接表中即可達到這個目的

技術分享圖片
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 10;
struct EDGE{ int v, nxt, id; bool used; }Edge[maxn<<2];
int Head[30], cnt, N;
int IN[30], OUT[30];
int St;
int ans[maxn<<2], len;
string str[maxn];

inline void init()
{
    memset(Head, -1, sizeof(Head));
    memset(IN, 0, sizeof(IN));
    memset(OUT, 0, sizeof(OUT));
    cnt = 0;
    St = 0x3f3f3f3f;
}

inline void AddEdge(int From, int To, int ID)
{
    Edge[cnt].used = false;
    Edge[cnt].id = ID;
    Edge[cnt].v = To;
    Edge[cnt].nxt = Head[From];
    Head[From] = cnt++;
}

void DFS(int v, int id)///由於是要邊路徑而不是頂點路徑,所以要帶個邊參數
{
    for(int i=Head[v]; i!=-1; i=Edge[i].nxt){
        if(!Edge[i].used){
            Edge[i].used = true;
            int Eiv = Edge[i].v;
            DFS(Eiv, Edge[i].id);
        }
    }
    if(id != -1)
        ans[len++] = id;

//    for(int i=Head[v]; i!=-1; i=Edge[i].nxt){///如果不像代邊的編號參數,可以這樣寫
//        if(!Edge[i].used){                   ///也可以達到記錄邊路徑的作用
//            Edge[i].used = true;
//            DFS(Edge[i].v);
//            ans[len++] = Edge[i].id;
//        }
//    }
}

int main(void)
{
    int nCase;
    scanf("%d", &nCase);
    while(nCase--){

        init();

        scanf("%d", &N);
        for(int i=0; i<N; i++)
            cin>>str[i];
        sort(str, str+N);

        for(int i=N-1; i>=0; i--){///將邊從大的到小的插入,因為是鏈式向前星存儲
            int Len = str[i].length();
            int From = str[i][0] - a;
            int To = str[i][Len-1] - a;
            AddEdge(From, To, i);
            IN[To]++, OUT[From]++;
            St = min(To, min(St, From));///記錄一下DFS的起點,對應了有歐拉回路的情況
        }

        int Not_equal, St_num, Des_num;
        Not_equal = St_num = Des_num = 0;

        for(int i=0; i<26; i++){
            if(!IN[i] && !OUT[i]) continue;
            if(IN[i] != OUT[i]) Not_equal++;
            if(OUT[i] - IN[i] == 1){
                St_num++;
                St = i;///此時已經可以判定沒有歐拉回路了,改變起點,將出度多的作為起點
            }else if(IN[i] - OUT[i] == 1)
                Des_num++;
        }

        if(Not_equal > 0){///如果有點的出入度不想等
            if(!(Not_equal == 2 && St_num == 1 && Des_num == 1)){///並沒有歐拉路徑
                puts("***");
                continue;
            }
        }

        len = 0;
        DFS(St, -1);///恢復歐拉路徑

        if(len != N){
            puts("***");
            continue;
        }

        for(int i=len-1; i>=0; i--){
            if(ans[i] == -1) continue;
            cout<<str[ans[i]];
            if(i > 0) putchar(.);
            else puts("");
        }
    }
    return 0;
}
/*
2
6
aloha
arachnid
dog
gopher
rat
tiger
3
oak
maple
elm
*/
View Code

歐拉回路 && 歐拉路徑