1. 程式人生 > >歐拉回路(尤拉路徑)

歐拉回路(尤拉路徑)

定義

給一個連通圖,求一條每條邊恰好走一次的路徑,就叫尤拉路徑,如果要求回到原點,就叫歐拉回路。(也叫一筆畫問題)

推論

無向圖

把度數為偶數的點叫偶點,度數為奇數的點叫奇點。

  • 如果一個圖存在尤拉路徑,則奇點的個數一定為0個或2個。
    白話證明:
    • 奇點個數為0個時,所有點都為偶點。
      則可以使得每個點的入邊數量與出邊數量相等,一定存在一個環。
      走完這個環後,每個點剩餘的邊一定是偶數,說明從這個點出發一定可以走出一個環再回到這個點,可以將這個新的環與原來的環拼接,形成新的路徑,從而遍歷所有邊。
    • 如果奇點個數為2個,將這兩個邊連線,形成↑上面的樣子,保證最後走新連的邊,就可以完成尤拉路徑。
  • 只有奇點為0個的圖存在歐拉回路,證明同上。
  • 奇點有2個時,必然一個是起點,一個是終點。

有向圖

  • 只有每個點的出度等於入度時才存在歐拉回路
    (迴路必然有進有出,肯定要相等啊,,,)
  • 如果存在一個點的入度比出度大1,另一個點出度比入度大1,則存在尤拉路徑。(證明通無向圖)

求法

消圈法

如同前面無向圖歐拉回路證明一樣,找一個圈,中途把其它的圈並上來,併成一條路徑,有遞迴版本和迴圈版本。
此演算法只能計算確定有尤拉路徑的圖

程式碼

遞迴
#include<cstdio>
#include<algorithm>
using namespace
std; const int MAXN=505,MAXE=1100; int E[MAXN][MAXN]; int ans[MAXE],cnt; int deg[MAXN],n,m,S,T; void euler(int id) { for(int i=1;i<=n;i++) if(E[id][i]>0) { E[id][i]--; E[i][id]--; euler(i); } ans[++cnt]=id; } int main() { int
a,b; scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%d%d",&a,&b); E[a][b]++; E[b][a]++; deg[a]++; deg[b]++; n=max(n,a); n=max(n,b); } S=0;T=0; for(int i=1;i<=n;i++) if(deg[i]&1) { if(S) { T=i; break; } S=i; } if(S) euler(S); else euler(1); for(int i=cnt;i>=1;i--) printf("%d\n",ans[i]); return 0; }
迴圈
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=505,MAXE=1100;
int E[MAXN][MAXN];
int ans[MAXE],cnt;
int deg[MAXN],n,m;
int stack[MAXE],top;
void find_circle(int S)
{
    int x=S;
    bool flag=true;
    while(flag)
    {
        stack[++top]=x;
        flag=false;
        for(int i=1;i<=n;i++)
            if(E[x][i]>0)
            {
                E[x][i]--;
                E[i][x]--;
                x=i;
                flag=true;
                break;
            }
    }
    if(stack[top]==S)
        top--;
}
void euler(int S)
{
    find_circle(S);
    while(top)
    {
        ans[++cnt]=stack[top];
        top--;
        find_circle(stack[top+1]);
    }
}
int main()
{
    int a,b,S,T;
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&a,&b);
        E[a][b]++;
        E[b][a]++;
        deg[a]++;
        deg[b]++;
        n=max(n,a);
        n=max(n,b);
    }
    S=0;T=0;
    for(int i=1;i<=n;i++)
        if(deg[i]&1)
        {
            if(S)
            {
                T=i;
                break;
            }
            S=i;
        }
    if(S)
        euler(S);
    else
        euler(1);
    for(int i=cnt;i>=1;i--)
        printf("%d\n",ans[i]);
    if(!S)
        printf("1\n");
    return 0;
}