【小结】欧拉路

欧拉路定义:每条边恰好走一遍的路径,回到原点的是欧拉回路(欧拉环),否则为欧拉路。

性质:

无向:度数全都为 \(0\)(欧拉环)或有两个度数为奇数(一个起点一个终点)

有向:入度全部等于初度(欧拉环),或有一个出度 - 入度 = \(1\),且有一个入度 - 初度 = \(1\) 的(起点、终点)。

写法:

都是按以上要求找到起点,然后跑 dfs。

其实本质都是一个环/一个起点,一个终点。无向图任选一个起点。欧拉回路任选一个度不为 \(0\) 的点。

欧拉路,一定是若干个环套环,套上至多 \(1\) 条路径。

欧拉路的写法,就是先走到底,再回溯时入栈,最后栈逆序输出。

为什么不能直接走到就输出?

因为有可能直接走到路径上,这样就走到了死胡同,出不来。

一个欧拉回路的必须走法就是先走环,回到若干次原点,然后最后走那条路经。

这样走死的只能先是那条路径。

若先走到的是一个环,那么这个环会走回原点,没有走到路径说明原点还有路,一定不会先回溯这个环。

每个环都是如此,所以最终一定会先回溯路径。

那么用栈正好逆序,走的路径也逆序,就正好构成了欧拉路径。

时间复杂度:\(O(n + m) = O(m)\)

P7771 【模板】欧拉路径

我的阶段:

10 分,判了 No

70 分,使用欧拉回路普通写法

AC,使用弧优化

为什么会这么慢?

因为同一个点可能有很多入度和出度,这就导致每次来这个点,都得判断是否能走,就算不行,也要花费很多时间。因为每来一次,都得判断好多遍。

那么我们直接判断过的不判断了,记录 \(f_i\) 表示 \(i\) 节点遍历过的边。下一个直接从 \(f_i\) 开始。

字典序问题:两次倒序,先选小的。(包括起点和走出去的点)。

#include<bits/stdc++.h>
using namespace std;
/*

*/
struct FSI{
	template<typename T>
	FSI& operator >> (T &res){
		res=0;T f=1;char ch=getchar();
		while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
		while (isdigit(ch)){res=res*10+ch-'0';ch=getchar();}
		res*=f;
		return *this;
	}
}scan;
const int N=1e5+10,M=2e5+10;
int n,m,i,j,din[N],dout[N];
int f[N];
int x,y;
int c1,c2;
int pos;
vector<int> G[N];
int st[M],tp;
void dfs(int x)
{
	for (;f[x]<G[x].size();) dfs(G[x][f[x]++]);
	st[++tp]=x;
}
int main()
{
	scan>>n>>m;
	for (i=1;i<=m;i++)
	{
		scan>>x>>y;
		G[x].push_back(y);
		dout[x]++;
		din[y]++;
	}
	for (i=1;i<=n;i++)
	{
		if (dout[i]-din[i]==1) c1++;
		else if (din[i]-dout[i]==1) c2++;
		else if (din[i]!=dout[i])
		{
			printf("No\n");
			return 0;
		}
	}
	if (c1>1||c2>1)
	{
		printf("No");
		return 0;
	}
	pos=1;
	for (i=1;i<=n;i++)
	{
		if (dout[i]-din[i]==1) pos=i;
	}
	for (i=1;i<=n;i++) sort(G[i].begin(),G[i].end());
	dfs(pos);
	for (i=tp;i>=1;i--) printf("%d ",st[i]);
	return 0;
}
posted @ 2026-08-21 16:52  GUO120822  阅读(1)  评论(0)    收藏  举报