學長(姐?)昨晚講了歐拉路,寫個博加深一下印象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; }
不知道之前有沒有人這麼幹的