歐拉回路(尤拉路徑)
阿新 • • 發佈:2019-02-08
定義
給一個連通圖,求一條每條邊恰好走一次的路徑,就叫尤拉路徑,如果要求回到原點,就叫歐拉回路。(也叫一筆畫問題)
推論
無向圖
把度數為偶數的點叫偶點,度數為奇數的點叫奇點。
- 如果一個圖存在尤拉路徑,則奇點的個數一定為0個或2個。
白話證明:
- 奇點個數為0個時,所有點都為偶點。
則可以使得每個點的入邊數量與出邊數量相等,一定存在一個環。
走完這個環後,每個點剩餘的邊一定是偶數,說明從這個點出發一定可以走出一個環再回到這個點,可以將這個新的環與原來的環拼接,形成新的路徑,從而遍歷所有邊。 - 如果奇點個數為2個,將這兩個邊連線,形成↑上面的樣子,保證最後走新連的邊,就可以完成尤拉路徑。
- 奇點個數為0個時,所有點都為偶點。
- 只有奇點為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;
}