欧拉回路算法
本文为旧文重置版。
本文尝试还原求解欧拉回路的线性算法的推导动机。
算法流程
本节推导的是无向图的欧拉回路算法。有向图的欧拉回路算法与无向图基本一致。
首先,观察到孤点与欧拉回路无关,可以认为图中没有孤点,或
考虑重述“
考虑分析欧拉图
考虑证明满足以上条件的
考虑重述“
不难发现
至此,我们已经得到了求解欧拉回路/路径的多项式时间的算法。
考虑直接设计一个求欧拉路径的算法,这样的算法也能求出欧拉回路。
如果
考虑子问题-分治-倍增-递归。任取
考虑如何处理这个问题。如果图
引理:欧拉图
所以,如果
所以,在这种情况下,可以先递归求出
这样我们就得到了一个
考虑进一步优化。考虑不要判定图是否连通。我们直接设
代码实现
模板题:[UOJ 117] 欧拉回路。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define sz(x) (int)(x).size()
using ll = long long;
const int kn = 1e5 + 10, km = 4e5 + 10;
int sid, n, m, d[kn];
vector<pair<int, int>> adj[kn];
vector<int> ans;
namespace undir {
bool vis[km];
void euler(int u) {
while (sz(adj[u])) {
auto [v, id] = adj[u].back();
adj[u].pop_back();
if (vis[id]) continue;
vis[id ^ 1] = true;
euler(v);
ans.push_back(id % 2 == 0 ? id / 2 : -(id / 2));
}
}
void solve() {
rep (i, 1, m) {
int u, v;
cin >> u >> v;
adj[u].push_back({v, 2 * i});
adj[v].push_back({u, 2 * i + 1});
++d[u], ++d[v];
}
rep (i, 1, n)
if (d[i] % 2 == 1) {
cout << "NO\n";
exit(0);
}
int s = 1;
while (s <= n && !sz(adj[s])) ++s;
euler(s);
}
} // namespace undir
namespace dir {
void euler(int u) {
while (sz(adj[u])) {
auto [v, id] = adj[u].back();
adj[u].pop_back();
euler(v);
ans.push_back(id);
}
}
void solve() {
rep (i, 1, m) {
int u, v;
cin >> u >> v;
adj[u].push_back({v, i});
++d[u], --d[v];
}
rep (i, 1, n)
if (d[i]) {
cout << "NO\n";
exit(0);
}
int s = 1;
while (s <= n && !sz(adj[s])) ++s;
euler(s);
}
} // namespace dir
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> sid >> n >> m;
if (sid == 1) undir::solve();
else dir::solve();
if (sz(ans) < m) {
cout << "NO\n";
return 0;
}
cout << "YES\n";
for (int i = sz(ans) - 1; i >= 0; --i) cout << ans[i] << " \n"[i == 0];
}