【模板】欧拉路
原“行列式”部分 merged into https://www.cnblogs.com/caijianhong/p/18322302
定义
- 欧拉回路:是图上的一条路径,满足每条边恰好在路径上出现一次(重边算不同的边)。还要满足路径的起点等于终点。
- 欧拉路径:是图上的一条路径,满足每条边恰好在路径上出现一次(重边算不同的边)。
这里没有指明图是有向图还是无向图,所以要分开讨论。另外默认将所有孤立点丢掉后图是联通的,意思是无向图的每对点都能到达,有向图将所有有向边换为无向边后无向图连通。
判定
- 无向图欧拉回路存在,当且仅当每个点的度数为偶数。
- 无向图欧拉路径存在,当且仅当它存在欧拉回路,或者恰好有两个点的度数为奇数(相当于删掉了一条边)。
- 有向图欧拉回路存在,当且仅当每个点的入度等于出度。
- 有向图欧拉路径存在,当且仅当它存在欧拉回路,或者恰好有一个点满足入度为出度加一,恰好有一个点满足入度为出度减一,剩下的所有点的入度等于出度。(也是删掉一条边)
Hierholzer 算法寻找欧拉路
从有向图的欧拉回路开始探索,怎么找出这个有向图的欧拉回路?
保证有解后,我们可以在图上随意选一个环,将环加入欧拉回路,然后继续拆环上的点,把点又拆成环,插进去,这是一个递归的算法。
我们仔细考虑一下,提出这样的算法:在全局维护当前的欧拉回路。dfs(u)
表示搜索到点 \(u\),欲将求出欧拉回路,随意选择一个点开始 dfs
。枚举一条还未访问的边 \((u, v)\),然后 dfs(v)
,然后将边 \((u, v)\) 插入欧拉回路的最前面。我不知道它为什么是对的,但他是对的。
- 对于最小字典序的欧拉路,要将所有点的出边按照指向点的字典序排序,然后选择一个最小字典序的起点出发。
- 有向图欧拉路径:从“满足入度为出度减一”的点开始搜索,按照有向图的欧拉回路的方法找,最终可以找到。
- 无向图欧拉回路:“枚举一条还未访问的边 \((u, v)\)”这一步,只需要控制好同一条无向边不被访问多次即可。
- 无向图欧拉路径:从任意一个度数为奇数的点开始搜索。
注意
仅当存在欧拉路时,Hierholzer 算法才可以正确运行。如果没有欧拉路,此算法可能返回任意解或产生运行时错误。若不存在欧拉路但你还想找(典型的如无向图定向),可以建立虚拟源点,将奇数度的点与虚拟源点连接,使所有点度数为偶数。
代码
有向图 / 无向图欧拉回路(UOJ 117)
有一个绷不住的问题:讨论 你是否还在写,假的欧拉回路 - Luogu Spilopelia
链式前向星,由于可能走到重复点,跳 nxt 的时候可能会越界。但反正我从 0 开始我没事。
点击查看代码
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
typedef long long LL;
template <int N, int M, class T = int>
struct graph {
int head[N + 10], nxt[M << 1], cnt;
struct edge {
int u, v;
T w;
} e[M << 1];
graph() { memset(head, cnt = 0, sizeof head); }
edge& operator[](int i) { return e[i]; }
void add(int u, int v, T w = 0) {
e[++cnt] = {u, v, w}, nxt[cnt] = head[u], head[u] = cnt;
}
void link(int u, int v, T w = 0) { add(u, v, w), add(v, u, w); }
};
int n, m, t, deg[1 << 17], stk[1 << 17], top;
graph<1 << 17, 1 << 18> g;
bool vis[1 << 19];
void dfs(int u) {
for (int& i = g.head[u]; i; i = g.nxt[i]) {
int v = g[i].v, j = i;
if (vis[i] || vis[i ^ 1]) continue;
vis[i] = true, dfs(v), stk[++top] = j;
//如果要输出点,stk[++top] = v,最后加入起点。
//注意记录边的编号的副本。
}
}
int main() {
scanf("%d%d%d", &t, &n, &m);
g.add(0, 0);
int last = -1;
for (int i = 1, u, v; i <= m; i++) {
scanf("%d%d", &u, &v);
last = u;
if (t == 1)
//无向图
g.link(u, v), deg[u]++, deg[v]++;
else
//有向图
g.add(u, v), g.add(0, 0), deg[u]++, deg[v]--;
}
for (int i = 1; i <= n; i++)
if (deg[i] % 2 || (t == 2 && deg[i])) return puts("NO"), 0;
if (~last) dfs(last);
if (top != m) return puts("NO"), 0;
puts("YES");
for (int i = top; i >= 1; i--)
printf("%d%c", stk[i] % 2 ? -stk[i] / 2 : stk[i] / 2, " \n"[i == 1]);
//就是输出 g[stk[i]].u -> g[stk[i]].v。
return 0;
}
欧拉回路计数
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/18322272