学长(姐?)昨晚讲了欧拉路,写个博加深一下印象ios
欧拉路是一种路(滑稽)。c++
一种不重不漏的一笔画走完全部路径,且每一条路都至少走一遍,也只能走一遍。数组
大约是这样的:函数
其中 1 -> 2 -> 3 -> 5 -> 10 -> 3 -> 4 -> 8 -> 9 -> 11 -> 8 -> 7 -> 6
就是一条欧拉路。优化
固然 1 -> 2 -> 3 -> 10 -> 5 -> 3 -> 4 -> 8 -> 11 -> 9 -> 8 -> 7 -> 6
也是一条欧拉路。spa
而后 6 -> 7 -> 8 -> 11 -> 9 -> 8 -> 4 -> 3 -> 10 -> 5 -> 3 -> 2 -> 1
因此反过来仍是欧拉路。code
由此能够看出,一个图的欧拉路并非惟一的。blog
而欧拉回路就是起点与终点相同的欧拉路,能够在欧拉路的起点与终点之间连一条边构成欧拉回路:get
思路:io
由欧拉路的性质可知,若是一个图(无向图)为欧拉路,那么这个图的全部点只有两个点的度数为奇数,或者没有点的度数为奇数,
其余全部点的度数为偶数。
而欧拉路的起点或终点就是那两个度数为奇数的点。(欧拉回路随便找个点当起点就行)
因此能够经过一个 dfs 来找出整个欧拉路。
先统计每一个点的度数;
再判断有几个点的度数为奇数:
- 若是有两个点或者没有点的度数为奇数,随便找个点做为起点开始 dfs;
- 若是有多个点的度数为奇数,输出
No answer
;dfs 过程当中用栈来维护欧拉路的路径;
最后输出路径。
完整代码 1(用邻接矩阵来存图):
#include <iostream> #include <cstdio> #define MAXN 1050 #define F1(i, a, b) for (int i = a; i <= b; ++i) #define F2(i, a, b) for (int i = a; i >= b; --i) using namespace std; int f[MAXN][MAXN]; // f[i][j] 存储从 i 到 j 之间有几条边 int du[MAXN], sta[MAXN]; // du[i] 存储 i 点的度;sta 数组为栈用来记录欧拉路的路径 int top = 0, n, m; void dfs(int t) { F1(i, 1, n) { // 挨个寻找与 t 链接的边 while (f[t][i] || f[i][t]) { // 若是这里有边,while 防止多边链接两点相同 --f[t][i]; // 该边数量减一,表明该边走过了 --f[i][t]; // 无向图 dfs(i); } } sta[++top] = t; // 回溯时把 t 点放进栈里 } int main() { int x, y, k = 1, cnt = 0; scanf("%d %d", &n, &m); F1(i, 1, m) { scanf("%d %d", &x, &y); ++f[x][y]; // 存边 ++f[y][x]; ++du[x]; // 统计度数 ++du[y]; } F2(i, 1, n) if (du[i] % 2) ++cnt; if (cnt == 2 || cnt == 0) dfs(k); else { printf("No answer\n"); return 0; } F2(i, top, 1) printf("%d\n", sta[i]); return 0; }
完整代码 2(链式前向星存图):
前置:
毫无疑问链式前向星存图必定是比邻接矩阵存图更优的,特别是在稀疏图中
但不少人不用链式前向星是由于欧拉路大部分是无向图,
标记一条道路被走过须要对这条道路的反向道路也标记为走过,
而这一点不太好实现,因此不少人直接用邻接矩阵来存图。
而这个时候就须要对链式前向星的理解力了:
链式前向星存的是边,同时用 head 数组来记录上一条边的序号,而遍历的时候用 head 数组和 nxt 来遍历,
存图用 add 函数来实现。
如今再回到欧拉路上:
欧拉路一般为无向图,若是用链式前向星来存,那么就会用 add 正着存一遍,再倒着存一遍,因此两条边的序号是连着的,
若是序号为奇数(我是从一开始存),那么这条边就是正着存的,那它的下一条边就是这条边的反边;
若是序号为偶数,那么这条边就是倒着存的,那它的上一条边就是这条边的正边。
代码:
#include <iostream> #include <cstdio> #define MAXN 1001 #define F1(i, a, b) for (int i = a; i <= b; ++i) #define F2(i, a, b) for (int i = a; i >= b; --i) using namespace std; int js = 0, n, m, top = 0; int head[MAXN], du[MAXN], sta[MAXN]; struct edge { int v, nxt; bool book; // book 记录该边是否被走过 }e[MAXN << 1]; inline int read() { // 读入优化 int sto = 0, fg = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') fg = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { sto = (sto << 1) + (sto << 3) + (ch ^ 48); ch = getchar(); } return sto * fg; } void add(int u, int v) { // 存边函数 e[++js].v = v; e[js].nxt = head[u]; head[u] = js; } void dfs(int t) { for (int i = head[t]; i; i = e[i].nxt) { if (!e[i].book) { // 用链式前向星遍历 if (i % 2) e[i + 1].book = 1; else e[i - 1].book = 1; // 将反向边标记 e[i].book = 1; // 正向边标记(好像没用) dfs(e[i].v); } } sta[++top] = t; // 存路 } int main() { int x, y, k = 1, cnt = 0; n = read(); m = read(); F1(i, 1, m) { x = read(); y = read(); add(x, y); // 存正边 add(y, x); // 存反边 ++du[x]; ++du[y]; } F1(i, 1, n) if (du[i] % 2) ++cnt, k = i; if (cnt == 2 || cnt == 0) dfs(k); else { printf("No answer\n"); return 0; } F2(i, top, 1) printf("%d ", sta[i]); return 0; }
不知道之前有没有人这么干的