【图论】总结 11:欧拉路径与欧拉回路
定义:给定一张图 \(G\),若存在一条从 \(S\) 到 \(T\) 的路径,恰好不重不漏地经过每一条边一次,则称该路径为 \(S\) 到 \(T\) 的欧拉路径。
定义:给定一张图 \(G\),若存在一条从 \(S\) 出发,恰好不重不漏地经过每一条边一次,最终回到 \(S\) 的回路,则称该回路为图 \(G\) 的欧拉回路。存在欧拉回路的无向图被称为欧拉图。
欧拉路径的存在性判定
对于一张连通的图,我们可通过节点的度数来判断图中是否存在欧拉路径。
定理:对于无向图,其存在欧拉路径的充分必要条件是:度数为奇数的点只能有 \(0\) 或 \(2\) 个。当奇点为 \(2\) 个时,这两个奇点就是欧拉路径的起点和终点。
简单证明一下,由于我们要找的欧拉路径满足恰好不重不漏地经过每一条边一次,那么对于一个节点,我们必然要经过其偶数次才能保证其能够进入一次然后出去一次,因此如果度数为奇数,那么其一定是出去了回不来或者进来了出不去,即起点和终点。
定理:对于有向图:其存在欧拉路径的充分必要条件是:所有点的入度 \(d^-(u)\) 都等于出度 \(d^+(u)\) 或者只有两个点 \(S,T\) 不满足上面的条件,而这两个点满足 \(d^+(S)=d^-(S)+1\),\(d^-(T)=d^+(T)+1\),那么 \(S\) 为起点,\(T\) 为终点。
证明方法同上,每个点出入度抵消才保证不重不漏,而不抵消也要保证至少相差 \(1\),这样才使起点多余的出度恰对应终点多余的入度。
欧拉回路的存在性判定
定理:对于无向图,其存在欧拉回路的充分必要条件是:没有度数为奇数的点。
定理:对于有向图,其存在欧拉回路的充分必要条件是:\(\forall u\in V,d^-(u)=d^+(u)\)。
可以发现,欧拉回路的存在性判定条件是比欧拉路径的判定条件更强的,因为欧拉回路本身就是一种特殊的欧拉路径嘛。
模板题 AcWing 1186:给定一个整数 \(t\),\(t\in\{1,2\}\),如果 \(t=1\),表示所给图为无向图,如果 \(t=2\),表示所给图为有向图。
接下来给定一张图的点数 \(n\) 和边数 \(m\),并给出 \(m\) 条无向(有向)边。图中可能有重边也可能有自环。点的编号从 \(1\) 到 \(n\)。
如果不存在欧拉回路,输出NO
;否则输出YES
,并输出具体方案。
判无解用上面的定理判断即可,此外还要判断边连不连通。
我们跑一遍 DFS,然后在每跑完一条边时就将它删去,时间复杂度 \(O(n+m)\)。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = 4e5 + 10;
int type;
int n, m;
int h[N], e[M], ne[M], idx;
bool used[M];//标记边是否走过
int ans[M / 2], cnt;//记录答案
int din[N], dout[N];//入度和出度
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
void dfs(int u)
{
for(int &i = h[u]; ~i;)
{
if(used[i])
{
i = ne[i];
continue;
}
used[i] = true;
if(type == 1) used[i ^ 1] = true;//对于无向图,反边也要标记
int t;
if(type == 1)
{
t = i / 2 + 1;
if(i & 1) t = -t;
}
else t = i + 1;
int j = e[i];
i = ne[i];
dfs(j);
ans[++ cnt] = t;
}
}
int main()
{
cin >> type;
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
for(int i = 0; i < m; i ++)
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
if(type == 1) add(b, a);
din[b] ++, dout[a] ++;
}
if(type == 1)
{
for(int i = 1; i <= n; i ++)
if(din[i] + dout[i] & 1)
{
puts("NO");
return 0;
}
}
else
{
for(int i = 1; i <= n; i ++)
if(din[i] != dout[i])
{
puts("NO");
return 0;
}
}
for(int i = 1; i <= n; i ++)
if(h[i] != -1)
{
dfs(i);
break;
}
if(cnt < m)//图不连通
{
puts("NO");
return 0;
}
puts("YES");
for(int i = cnt; i; i --) printf("%d ", ans[i]);//反向输出
return 0;
}