欧拉图
定义¶
通过图中所有边恰好一次且行遍所有顶点的通路称为欧拉通路。
通过图中所有边恰好一次且行遍所有顶点的回路称为欧拉回路。
具有欧拉回路的无向图称为欧拉图。
具有欧拉通路但不具有欧拉回路的无向图称为半欧拉图。
有向图也可以有类似的定义。
非形式化地讲,欧拉图就是从任意一个点开始都可以一笔画完整个图,半欧拉图必须从某个点开始才能一笔画完整个图。
性质¶
欧拉图中所有顶点的度数都是偶数。
若
若
判别法¶
求欧拉回路或欧拉路¶
题面
给定一张有 500 个顶点的无向图,求这张图的一条欧拉路或欧拉回路。如果有多组解,输出最小的那一组。
在本题中,欧拉路或欧拉回路不需要经过所有顶点。
边的数量 m 满足
Fleury 算法¶
也称避桥法,一个偏暴力的算法。算法流程为每次选择下一条边的时候优先选择不是桥的边。
一个广泛使用但是错误的实现方式是先 Tarjan 预处理桥边,然后再 DFS 避免走桥。但是由于走图过程中边会被删去,一些非桥边会变为桥边导致错误。最简单的实现方法是每次删除一条边之后暴力跑一遍 Tarjan 找桥,时间复杂度是
在解决本题的时候只需要再贪心就好,不过由于复杂度不对,还是看更优的算法吧。
逐步插入回路法(Hierholzer 算法)¶
算法流程为从一条回路开始,每次任取一条目前回路中的点,将其替换为一条简单回路,以此寻找到一条欧拉回路。如果从路开始的话,就可以寻找到一条欧拉路。
Hierholzer 算法的暴力实现如下:
这个算法的时间复杂度约为 stack<int>
,因为如果找的不是回路的话必须将那一部分放在最后。
如果需要输出字典序最小的欧拉路或欧拉回路的话,因为需要将边排序,时间复杂度是
对于这道例题的一个代码实现如下。注意,不能使用邻接矩阵存图,否则时间复杂度会退化为
代码实现
#include <algorithm>
#include <cstdio>
#include <stack>
#include <vector>
using namespace std;
struct edge {
int to;
bool exists;
int revref;
bool operator<(const edge& b) const { return to < b.to; }
};
vector<edge> beg[505];
int cnt[505];
const int dn = 500;
stack<int> ans;
void Hierholzer(int x) { // 关键函数
for (int& i = cnt[x]; i < (int)beg[x].size();) {
if (beg[x][i].exists) {
edge e = beg[x][i];
beg[x][i].exists = 0;
beg[e.to][e.revref].exists = 0;
++i;
Hierholzer(e.to);
} else {
++i;
}
}
ans.push(x);
}
int deg[505];
int reftop[505];
int main() {
for (int i = 1; i <= dn; ++i) {
beg[i].reserve(1050); // vector 用 reserve 避免动态分配空间,加快速度
}
int m;
scanf("%d", &m);
for (int i = 1; i <= m; ++i) {
int a, b;
scanf("%d%d", &a, &b);
beg[a].push_back((edge){b, 1, 0});
beg[b].push_back((edge){a, 1, 0});
++deg[a];
++deg[b];
}
for (int i = 1; i <= dn; ++i) {
if (!beg[i].empty()) {
sort(beg[i].begin(), beg[i].end()); // 为了要按字典序贪心,必须排序
}
}
for (int i = 1; i <= dn; ++i) {
for (int j = 0; j < (int)beg[i].size(); ++j) {
beg[i][j].revref = reftop[beg[i][j].to]++;
}
}
int bv = 0;
for (int i = 1; i <= dn; ++i) {
if (!deg[bv] && deg[i]) {
bv = i;
} else if (!(deg[bv] & 1) && (deg[i] & 1)) {
bv = i;
}
}
Hierholzer(bv);
while (!ans.empty()) {
printf("%d\n", ans.top());
ans.pop();
}
}
应用¶
有向欧拉图可用于计算机译码。
设有
构造如下有向欧拉图:
设
规定
顶点
边
这样的
任求
练习题¶
buildLast update and/or translate time of this article,Check the history
editFound smelly bugs? Translation outdated? Wanna contribute with us? Edit this Page on Github
peopleContributor of this article OI-wiki
translateTranslator of this article Visit the original article!
copyrightThe article is available under CC BY-SA 4.0 & SATA ; additional terms may apply.