1. 程式人生 > 其它 >歐拉回路學習筆記

歐拉回路學習筆記

歐拉回路是從圖上某一點出發,經過一系列不重複的邊,然後再回到開始節點的路徑。

1.定義

通俗的講,歐拉回路是從圖上某一點出發,經過一系列不重複的邊,然後再回到開始節點的路徑。
注意,這個過程可以經過重複的節點 比如圖中僅有一個點,有 \(998244353\) 個自環,但這些自環仍然可以構成歐拉回路。

2.存在條件

  • 有向圖
    每個節點入度等於出度
  • 無向圖
    每個節點度均為偶數

3.求解方法

比較簡單,從一個點出發進行dfs,在回溯的時候把當前邊加入答案佇列。
但有以下情況需要考慮

  • 圖不連通
    無解。前提是至少兩個連通塊內有邊
  • 單獨一個點
    可以忽略,不對答案產生影響。因為歐拉回路關心的是經過所有邊 單獨一個點不會影響答案
  • 優化
    如果不加當前弧優化,複雜度是 \(O(nm)\)

    加上後能優化到 \(O(n+m)\)

4.例題&程式碼

模板-UOJ117
分別對有向圖和無向圖找出歐拉回路,輸出路徑。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<map>
#include<cstring>
#include<stack>
#include<queue>
#include<vector>
#include<bitset>
#define ll long long
using namespace std;
const int inf = 0x7fffffff;
int T,n,m;
#define maxn 400009
int in[maxn],ans[maxn],out[maxn];
int nxt[maxn],head[maxn],to[maxn];
bool ban[maxn],vis[maxn];
namespace fuckccf//無向邊 
{
	int num=0,cnt=1;//!!!從1開始記錄 
	void Add(int x,int y){cnt++;nxt[cnt]=head[x];to[cnt]=y;head[x]=cnt;}//鄰接表 
	void dfs(int x)
	{
		ban[x]=1;
		for(int &i=head[x];i;i=nxt[i])//當前弧 
		{
			int t=to[i],j=i>>1,val=i;//!!!進行完dfs之後i的值會改變 要提前存下來 
			if(vis[j])continue;
			vis[j]=1;
			dfs(t);
			num++;
			if(val&1)ans[num]=-j;//如果是正向的 i末尾是0 否則是1 
			else ans[num]=j;
		}
	}
	void main()
	{
		scanf("%d%d",&n,&m);
		for(int i=1;i<=m;i++)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			Add(x,y);
			Add(y,x);
			in[x]++;
			in[y]++;
		}
		for(int i=1;i<=n;i++)if(in[i]&1){printf("NO\n");return;}//判斷無解 
		for(int i=1;i<=n;i++)
		{
			if(!ban[i])dfs(i);
			if(num&&num<m)//走了一些邊,但沒走完,說明圖不連通或者無解 
			{
				printf("NO\n");
				return;
			}
		}
		printf("YES\n");
		
		for(int i=m;i>=1;i--)
		{
			printf("%d ",ans[i]);
		}
	}
}
namespace tymXINzhy//有向邊 
{
	int num=0,cnt=0;
	void Add(int x,int y){cnt++;nxt[cnt]=head[x];to[cnt]=y;head[x]=cnt;}
	void dfs(int x)
	{
		ban[x]=1;
		for(int &i=head[x];i;i=nxt[i])
		{
			int t=to[i],val=i;
			if(vis[i])continue;
			vis[i]=1;
			dfs(t);
			num++;
			ans[num]=val;//直接記錄邊即可 
		}
	}
	void main()
	{
		scanf("%d%d",&n,&m);
		for(int i=1;i<=m;i++)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			Add(x,y);
			in[y]++;
			out[x]++;
		}
		
		for(int i=1;i<=n;i++)if(in[i]!=out[i]){printf("NO\n");return;}
		for(int i=1;i<=n;i++)
		{
			if(!ban[i])dfs(i);
			if(num&&num<m)
			{
				printf("NO\n");
				return;
			}
		}
		printf("YES\n");
		for(int i=m;i>=1;i--)
		{
			printf("%d ",ans[i]);
		}
	}
}
signed main()
{
//	freopen("h.in","r",stdin);
	scanf("%d",&T);
	if(T==1)fuckccf::main();
	else tymXINzhy::main();
	return 0;
}
`