【模板】欧拉路

原“行列式”部分 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;
}


欧拉回路计数

https://www.cnblogs.com/caijianhong/p/17571788.html

posted @ 2024-07-25 09:23  caijianhong  阅读(25)  评论(0编辑  收藏  举报