图中经过所有边恰好一次的路径叫欧拉路径(也就是一笔画)。如果此路径的起点和终点相同,则称其为一条欧拉回路。
欧拉图中所有顶点的度数都是偶数。
若 G G G 是欧拉图,则它为若干个环的并,且每条边被包含在奇数个环内。
当然,一副图有欧拉路径,还必须满足将它的有向边视为无向边后它是连通的(不考虑度为 0 0 0 的孤立点),连通性的判断我们可以使用并查集
或 dfs
等。
存在欧拉回路(即满足存在欧拉回路的条件),也一定存在欧拉路径。
寻找欧拉路径(默认存在):
首先根据题意以及判定先确定起点 S S S。
从起点 S S S 开始 dfs
。
dfs
伪代码如下:
void dfs(int now){
枚举 now 的出边。
如果该边还未被访问
标记为已访问
dfs(该边连向的另一个点)
now入栈
}
最后倒序输出栈内的所有节点即可。
感性理解倒序输出的原因:如果是欧拉回路,那么遍历到哪,输出到哪也是对的,因为不管怎么走都会绕某个环走回起点,所以不到最后不会出栈,然而欧拉路径会出现边都被走过了,走不回起点,最后会停留在终点,遇到这种情况这种路径会最先出栈,于是只要把这个路径先走了,前面就和欧拉回路一样随便走就行,不会出栈,于是倒序输出就是对的。
题目描述
求有向图字典序最小的欧拉路径。
输入格式
第一行两个整数 n , m n,m n,m 表示有向图的点数和边数。
接下来 m m m 行每行两个整数 u , v u,v u,v 表示存在一条 u → v u\to v u→v 的有向边。
输出格式
如果不存在欧拉路径,输出一行
No
。否则输出一行 m + 1 m+1 m+1 个数字,表示字典序最小的欧拉路径。
题意:给定 n n n 个点, m m m 条边,求这副有向图字典序最小的欧拉路径。
思路:
本题需要判断并找出有向图的欧拉路径。
由于本题保证“将有向边视为无向边后图连通”,所以判定时不用判断连通性。
还有一点要注意的是本题需要按照字典序输出。
这一点如何解决呢?
法一:
直接使用数组存邻接矩阵,枚举点 x x x 出边时,直接枚举编号从 1 1 1 到 n n n 的点 y y y,再判断 x x x, y y y 之间是否有未访问边,这样就解决了字典序的问题。
dfs 代码(对应伪代码):
void dfs(int now){
for(int i = 1 ; i <= n ; i ++)
if(G[u][i] > 0)
G[u][i]--,dfs(i);
st.push(now);
}
但是这样的做法时间复杂度为 O ( n 2 ) O(n^2) O(n2),显然会超时。
法二:
既然邻接矩阵不行,那我们就用时间复杂度更优的邻接表,将 n o w now now 的所有出边排序即可。链式前向星对于排序出边的操作有些困难,而 vector
则容易得多,所以选用 vector
。
sort 代码:
for(int i = 1 ; i <= n ; i ++)sort(G[i].begin(), G[i].end());
dfs 代码:
void dfs(int now){
for(int i = a[now] ; i < g[now].size() ; i = a[now])
a[now] = i + 1,dfs(g[now][i]);
s.push(now);
}
//其中 a[now] 表示 G[now][1,2……,a[now] - 1] 都已经被标记访问过,下一次要从 G[now][a[now]] 开始访问。
dfs 时间复杂度: O ( n ) O(n) O(n)。
sort 时间复杂度: O ( m log m ) O(m\log m) O(mlogm)。
总时间复杂度: O ( n + m log m ) O(n+m\log m) O(n+mlogm)。
足以 AC 本题。
#include
#include
#include
#include
#define MAXN 100005
using namespace std;
int n, m, u, v;
int cnt1, cnt2, tmp = 1;
int a[MAXN], rd[MAXN], cd[MAXN];
bool flag = true;
stack <int> s;
vector <int> g[MAXN];
int read(){
int t = 1, x = 0;char ch = getchar();
while(!isdigit(ch)){if(ch == '-')t = -1;ch = getchar();}
while(isdigit(ch)){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * t;
}
void write(int x){
if(x < 0){putchar('-');x = -x;}
if(x >= 10)write(x / 10);
putchar(x % 10 ^ 48);
}
void dfs(int now){
for(int i = a[now] ; i < g[now].size() ; i = a[now])
a[now] = i + 1,dfs(g[now][i]);
s.push(now);
}
int main(){
n = read();m = read();
for(int i = 1 ; i <= m ; i ++){
u = read();v = read();
g[u].push_back(v);
cd[u]++;rd[v]++;
}
for(int i = 1 ; i <= n ; i ++)sort(g[i].begin(), g[i].end());
for(int i = 1 ; i <= n ; i ++){
if(cd[i] != rd[i])flag = false;
if(rd[i] - cd[i] == 1)cnt1++;
if(cd[i] - rd[i] == 1)
cnt2++,tmp = i;
}
if(flag == false)
if(cnt1 != cnt2 || cnt1 != 1){
puts("No");return 0;
}
dfs(tmp);
while(!s.empty())
write(s.top()),putchar(' '),s.pop();
return 0;
}