题解【模板】有向图欧拉路径

· · 题解

题解【模板】有向图欧拉路径

### 前置芝士: 1. **欧拉路径** - 定义:从一个点出发,不重不漏的经过图中每一条边的一条路径(允许多次经过同一个点)。 **判断方法** - 若为无向图,则需连通,且图中恰好存在两个点的度数是奇数,其他节点的度数为偶数,这两个度数为奇数的点就是起点与终点;或者所有点度数都是偶数。 - 若为有向图,则需连通,且图中恰好存在一个点入度比出度多一,一个点出度比入度多一,其他节点的出度等于入度;或者所有点入度等于出度。 2. **欧拉回路** - 定义:起点和终点是一个点的欧拉路径。 - 若为无向图,则需连通,且所有点的度数都是偶数。 - 若为有向图,则需连通,且所有点的入度等于出度 在本题中,我们要在一个**有向图**中找到字典序最小的**欧拉路径**。 ## Solution 既然是【模板】了,那就直接讲具体做法了。 对于欧拉路径,我们一般采用 **$\mathrm{Hierholzer}$ 算法** - 首先我们要确定出起点,然后从起点开始不走重复边的递归。 - 对于每一个点,当访问完所有的边时,把这个点加入答案序列。 - 最后,输出答案序列即为一条欧拉路径依次经过的点。 至此,我们可以写出的基本框架了: ```cpp void dfs(int u){ for(){ //遍历所有与u相邻的点v if(!vis[v]){ //u与v的这条边没被访问过 vis[v]=1; dfs(v); //递归与u相邻的点v } } s.push(u); //没有边了,将u进入序列 } ``` 但这样**时间复杂度**是不对的。按照算法,我们不能走重复边,但如果每次都vis判断一遍,则会出现很多次访问同一条边,时间复杂度是不正确的。 #### 处理: 对于任意一个点 $u$ 来说,建完图之后访问边是有顺序的,我们新开一个数组 $delete $ 记录访问到哪个位置即可(即该删除哪个位置)。 这题另一个 ex 的地方还是在于**按照字典序输出** 。 ## Code #### 判断是否有欧拉路径部分: ```cpp //有向图欧拉路径:一个点入度=出度+1,一个点出度=入度+1,其余点(或所有点)入度=出度 bool flag1=1;//所有点入度是否出度 int sum1=0,sum2=0;//出度-入度=1的数量 和 入度-出度=1的数量 for(int i=1;i<=n;i++){ if(chu[i]!=ru[i]){ flag1=0; } if(chu[i]-ru[i]>1){ cout<<"No"; return 0; } if(chu[i]-ru[i]==1){ start=i; sum1++; } if(ru[i]-chu[i]==1){ sum2++; } } if(!(flag1==1||(sum1==sum2)&&sum1==1)){ cout<<"No"; return 0; } ``` #### 邻接矩阵 60pts: ```cpp #include <bits/stdc++.h> using namespace std; const int N=1e4+10; int ru[N],chu[N],mapp[N][N],n,m; int delet[N]; //记录该访问(删除)哪个点 stack <int> s; void dfs(int u){ for(int v=1;v<=n;v++){ //遍历所有点 if(mapp[u][v]>0){ //有边,说明u与v相连 mapp[u][v]--; //删边 dfs(v); //递归 } } s.push(u); } int main(){ cin>>n>>m; int u,v; for(int i=1;i<=m;i++){ cin>>u>>v; mapp[u][v]++; // 邻接矩阵 chu[u]++,ru[v]++; //统计点的出度和入度 } int start=1; //判断部分 dfs(start); while(!s.empty()){ cout<<s.top()<<" "; s.pop(); } return 0; } ``` 为什么这里要提到邻接矩阵呢,因为邻接矩阵有个极大的优点就是**不需要排序**(类似于桶),不用考虑字典序的问题。 ### vector 模拟邻接表 100pts 注:vector的排序方法 ```cpp sort(a.begin(),a.end(),cmp); ``` ------------ ```cpp #include <bits/stdc++.h> using namespace std; const int N=1e5+10; int ru[N],chu[N],n,m; stack <int> s; int delet[N]; vector <int> a[N]; void dfs(int u){ for(int i=delet[u];i<a[u].size();i=delet[u]){ delet[u]=i+1; dfs(a[u][i]); } s.push(u); } int main(){ cin>>n>>m; int u,v; for(int i=1;i<=m;i++){ cin>>u>>v; a[u].push_back(v); chu[u]++,ru[v]++; } for(int i=1;i<=n;i++){ sort(a[i].begin(),a[i].end()); } int start=1; bool flag1=1; int sum1=0,sum2=0; for(int i=1;i<=n;i++){ if(chu[i]!=ru[i]){ flag1=0; } if(chu[i]-ru[i]>1){ cout<<"No"; return 0; } if(chu[i]-ru[i]==1){ start=i; sum1++; } if(ru[i]-chu[i]==1){ sum2++; } } if(!(flag1==1||(sum1==sum2)&&sum1==1)){ cout<<"No"; return 0; } dfs(start); while(!s.empty()){ cout<<s.top()<<" "; s.pop(); } return 0; }