【算法】浅谈欧拉路径

· · 个人记录

## $\text{Part 1}$ 一些概念 **欧拉路径**是什么呢? 其实就是一笔画问题。 抽象成数学语言: **通过图中所有边恰好一次且行遍所有顶点的通路称为欧拉路径**。 如果起点和终点相同,该路径又称为**欧拉回路**。 ## $\text{Part 2}$ 欧拉路径的判断 ### $\text{Part 2.1}$ 无向图 - 其实就是标准的一笔画问题。 - 我们要计算每个点的**度**。 - 如果**度为奇数的点**的个数是 $0$ 或 $2$,那么就有欧拉路径。 ### $\text{Part 2.2}$ 有向图 - 如果是有向图,我们要计算每个点的**入度**和**出度**。 - 如果是欧拉路径,需要是如下两种情况之一。 - **起点的入度比出度小 $1$,终点的入度比出度大 $1$,其余的点入度等于出度**(普通欧拉路径)。 - **所有的点入度等于出度**(欧拉回路)。 ## $\text{Part 3}$ 代码实现 [P7771 【模板】欧拉路径](https://www.luogu.com.cn/problem/P7771) 有向图的欧拉路径。用栈维护路径。求字典序,所以排个序。 $nxt$ 数组保证都遍历到且不重复。 ```cpp #include <bits/stdc++.h> #define Rint register int using namespace std; const int N=1e5+5; int n,m,d[N][2],nxt[N]; stack<int>st; vector<int>t[N]; inline void dfs(int s) { for(Rint i=nxt[s];i<t[s].size();i=nxt[s])//i=nxt[s] 不能是 i++ 因为可能重复遍历 { nxt[s]=i+1; dfs(t[s][i]); } st.push(s); } int main() { scanf("%d%d",&n,&m); for(Rint i=1;i<=m;++i) { int u,v;scanf("%d%d",&u,&v); t[u].push_back(v); d[u][1]++;d[v][0]++;//d[][1]表示出度,d[][0]表示入度 } for(Rint i=1;i<=n;++i) sort(t[i].begin(),t[i].end());//排序以保证字典序最小 int fl=1,cnt1=0,cnt2=0,St=1;//St需要初始化,防止是欧拉回路 for(Rint i=1;i<=n;++i) { if(d[i][0]!=d[i][1])fl=0;//判断是不是欧拉回路 if(d[i][1]-d[i][0]==1)cnt1++,St=i;//起点 if(d[i][0]-d[i][1]==1)cnt2++;//终点 } if((!fl)&&!(cnt1==1&&cnt2==1))//判断是否有解 { cout<<"No";return 0; } dfs(St); while(!st.empty()) { cout<<st.top()<<" "; st.pop(); } return 0; } ```