1. 程式人生 > >尤拉圖總結

尤拉圖總結

---------------------------------------------------------------------------------------------
轉自:https://blog.csdn.net/u013480600/article/details/44805491 

---------------------------------------------------------------------------------------------

                                               尤拉圖詳解


        通過圖(無向圖或有向圖)中所有邊一次且僅一次行遍圖中所有頂點的通路稱為尤拉通路,通過圖中所有邊一次且僅一次行遍所有頂點的迴路稱為歐拉回路。具有歐拉回路的圖稱為尤拉圖(Euler Graph),具有尤拉通路而無歐拉回路的圖稱為半尤拉圖。


        1.定義


        尤拉通路(Euler tour)——通過圖中每條邊一次且僅一次,並且過每一頂點的通路。
        歐拉回路 (Eulercircuit)——通過圖中每條邊一次且僅一次,並且過每一頂點的迴路。


        2.無向圖是否具有尤拉通路或迴路的判定


        G有尤拉通路的充分必要條件為:G 連通,G中只有兩個奇度頂點(它們分別是尤拉通路的兩個端點)。
        G有歐拉回路(G為尤拉圖):G連通,G中均為偶度頂點。


        3.有向圖是否具有尤拉通路或迴路的判定


        D有尤拉通路:D連通,除兩個頂點外,其餘頂點的入度均等於出度,這兩個特殊的頂點中,一個頂點的入度比出度大1,另一個頂點的入度比出度小1。
        D有歐拉回路(D為尤拉圖):D連通,D中所有頂點的入度等於出度。


        (注意:這裡說有向圖連通,說的是有向圖是弱連通圖。即把有向圖中的邊變成無向邊,只要該圖連通,那麼原有向圖即是弱連通圖。實際中可用並查集判斷是否弱連通)


         注意下面列印節點的程式碼,其實列印尤拉路徑的節點軌跡可以先列印每條尤拉路上的邊,然後取每條邊的頭結點即可(最後一條邊還需要取尾部結點)。


         對於一般的單詞首尾相連的問題,一般都是轉化為有向圖的尤拉通路問題(而不是無向圖),比如”給你多個單詞,問你能不能將所有單片語成這樣一個序列:序列的前一個單詞的尾字母與後一個單詞的頭字母相同”,如果你把每個單詞看出無向的邊,那麼最終求出的尤拉通路可能存在兩個單詞尾部和尾部相連的情況。

無向尤拉圖(半尤拉圖)逆序列印歐拉回路或通路程式碼:

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<iomanip>
#include<list>
#include<map>
#include<queue>
#include<sstream>
#include<stack>
#include<string>
#include<set>
#include<vector>
using namespace std;
#define PI acos(-1.0)
#define EPS 1e-8
#define MOD 1e9+7
#define LL long long
#define ULL unsigned long long     //1844674407370955161
#define INT_INF 0x7f7f7f7f      //2139062143
#define LL_INF 0x7f7f7f7f7f7f7f7f //9187201950435737471
const int dr[]={0, 0, -1, 1, -1, -1, 1, 1};
const int dc[]={-1, 1, 0, 0, -1, 1, -1, 1};
// ios::sync_with_stdio(false);
// 那麼cin, 就不能跟C的 scanf,sscanf, getchar, fgets之類的一起使用了。
const int maxn=100+5;

//無向圖列印尤拉路徑或迴路
//輸入保證是一個n頂點,m條邊的具有歐拉回路或尤拉路徑的無向圖

int n;//圖中頂點,頂點編號1到n
int m;//圖中邊
int G[maxn][maxn];
int vis[maxn][maxn];//vis[i][j]==1表示i與j點直接存在一條邊

//列印尤拉路徑或歐拉回路(必須本圖有尤拉路徑或迴路才行)
//列印的結果中最後一條邊才是u開始的,列印的第一條邊不一定是u開始的
//如果是列印尤拉路徑,那麼輸入的u一定要是起點之一,即度數為奇數的點之一
//否則euler列印的的邊不會構成尤拉路徑,只不過是亂序列印圖中所有的邊而已
void euler(int u)
{
    for(int v=1;v<=n;v++)if(vis[u][v]||vis[v][u])
    {
        //遞迴思想,去掉了u與v這條邊,
        //餘圖還是一個具有尤拉道路的圖,且v變成一個起點了
        vis[u][v]=vis[v][u]=0;
        euler(v);
        printf("%d %d\n",u,v);
    }
}

//輸出歐拉回路或路徑上按順序經過的節點
//u也必須要是起點之一,否則輸出的也是亂序點而已
void euler_point(int u)
{
    for(int v=1;v<=n;v++)if(vis[u][v] || vis[v][u])
    {
        vis[u][v]=vis[v][u]=0;
        euler_point(v);
    }
    printf("%d\n",u);
}

int main()
{
    while(scanf("%d%d",&n,&m)==2)
    {
        memset(G,0,sizeof(G));
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            G[u][v]=G[v][u]=1;//無向圖
            vis[u][v]=vis[v][u]=1;
        }

        int u;
        scanf("%d",&u);
        euler_point(u);

    }
    return 0;
}

有向尤拉圖(半尤拉圖)逆序列印歐拉回路或通路程式碼:
 

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<iomanip>
#include<list>
#include<map>
#include<queue>
#include<sstream>
#include<stack>
#include<string>
#include<set>
#include<vector>
using namespace std;
#define PI acos(-1.0)
#define EPS 1e-8
#define MOD 1e9+7
#define LL long long
#define ULL unsigned long long     //1844674407370955161
#define INT_INF 0x7f7f7f7f      //2139062143
#define LL_INF 0x7f7f7f7f7f7f7f7f //9187201950435737471
const int dr[]={0, 0, -1, 1, -1, -1, 1, 1};
const int dc[]={-1, 1, 0, 0, -1, 1, -1, 1};
// ios::sync_with_stdio(false);
// 那麼cin, 就不能跟C的 scanf,sscanf, getchar, fgets之類的一起使用了。
const int maxn=100+5;

//有向圖列印尤拉路徑或迴路
//輸入保證是一個n頂點,m條邊的具有歐拉回路或尤拉路徑的有向圖

int n;//圖中頂點,頂點編號1到n
int m;//圖中邊
int G[maxn][maxn];
int vis[maxn][maxn];//vis[i][j]==1表示i到j點存在一條邊

//列印尤拉路徑或歐拉回路(必須本圖有尤拉路徑或迴路才行)
//列印的結果中最後一條邊才是u開始的,列印的第一條邊不一定是u開始的
//如果是列印尤拉路徑,那麼輸入的u一定要是起點之一,即abs(入度-出度)==1
//且如果是出度-入度==1的點,那麼該點就應該是尤拉圖的起點,但是會逆序輸出,所以最後才輸出
//否則euler列印的的邊不會構成尤拉路徑,只不過是亂序列印圖中所有的邊而已
void euler(int u)
{
    for(int v=1;v<=n;v++)if(vis[u][v])
    {
        //遞迴思想,去掉了u與v這條邊,
        //餘圖還是一個具有尤拉道路的圖,且v變成一個起點了
        vis[u][v]=0;
        euler(v);
        printf("%d %d\n",u,v);
    }
}

//逆序輸出歐拉回路或路徑上按順序經過的節點
//u也必須要是起點之一,否則輸出的也是亂序點而已
void euler_point(int u)
{
    for(int v=1;v<=n;v++)if(vis[u][v])
    {
        vis[u][v]=0;
        euler_point(v);
    }
    printf("%d\n",u);
}

int main()
{
    while(scanf("%d%d",&n,&m)==2)
    {
        memset(G,0,sizeof(G));
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            G[u][v]=1;//有向圖
            vis[u][v]=1;
        }

        int u;
        scanf("%d",&u);
        euler(u);

    }
    return 0;
}