【小结】欧拉路
欧拉路定义:每条边恰好走一遍的路径,回到原点的是欧拉回路(欧拉环),否则为欧拉路。
性质:
无向:度数全都为 \(0\)(欧拉环)或有两个度数为奇数(一个起点一个终点)
有向:入度全部等于初度(欧拉环),或有一个出度 - 入度 = \(1\),且有一个入度 - 初度 = \(1\) 的(起点、终点)。
写法:
都是按以上要求找到起点,然后跑 dfs。
其实本质都是一个环/一个起点,一个终点。无向图任选一个起点。欧拉回路任选一个度不为 \(0\) 的点。
欧拉路,一定是若干个环套环,套上至多 \(1\) 条路径。
欧拉路的写法,就是先走到底,再回溯时入栈,最后栈逆序输出。
为什么不能直接走到就输出?
因为有可能直接走到路径上,这样就走到了死胡同,出不来。
一个欧拉回路的必须走法就是先走环,回到若干次原点,然后最后走那条路经。
这样走死的只能先是那条路径。
若先走到的是一个环,那么这个环会走回原点,没有走到路径说明原点还有路,一定不会先回溯这个环。
每个环都是如此,所以最终一定会先回溯路径。
那么用栈正好逆序,走的路径也逆序,就正好构成了欧拉路径。
时间复杂度:\(O(n + m) = O(m)\)。
P7771 【模板】欧拉路径
我的阶段:
为什么会这么慢?
因为同一个点可能有很多入度和出度,这就导致每次来这个点,都得判断是否能走,就算不行,也要花费很多时间。因为每来一次,都得判断好多遍。
那么我们直接判断过的不判断了,记录 \(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;
}

浙公网安备 33010602011771号