欧拉回路,欧拉路径
定义:
欧拉回路:每条边恰好只走一次,并能回到出发点的路径
欧拉路径:经过每一条边一次,但是不要求回到起始点
欧拉路径:经过每一条边一次,但是不要求回到起始点
无向图中:
1、欧拉路径:充要条件是度数为奇数的点的个数为0或2。
2、欧拉回路:充要条件是全部是偶点
有向图中:
1、欧拉路径:起点出度比入度大1,终点入度比出度大1,或者其他点入度初度都相等。
2、欧拉回路:每个点出度和入度都相等
两个算法:
Hierholzer算法自动寻找欧拉回路,在找不到欧拉回路的情况下会找到欧拉路径。前提是得给它指定好起点。
算法流程(无向图):
1.判断奇点数。奇点数若为0则任意指定起点,奇点数若为2则指定起点为奇点。
2.开始递归函数Hierholzer(x):
循环寻找与x相连的边(x,u):
删除(x,u)
删除(u,x)
Hierholzer(u);
将x插入答案队列之中
3.倒序输出答案队列
其实就是个dfs:
void dfs(int x){ rep(i, 1, n) if(ma[x][i]) ma[x][i] = ma[i][x] = 0, dfs(i); arr[++pos] = x; }
另一种是Fleury(弗罗莱)算法求欧拉路径
不介绍了,一搜一堆,贴个码。
void dfs(int x){ arr[top++] = x; rep(i, 1, n){ if(ma[i][x]){ ma[i][x]--; ma[x][i]--; dfs(i); } } } void fleury(int ss) { int brige; top = 0; arr[top++] = ss; // 将起点放入Euler路径中 while (top > 0) { brige = 1; rep(i, 1, n) { // 试图搜索一条边不是割边(桥) if (ma[arr[top-1]][i]) { brige = 0; break; } } if (brige) { // 如果没有点可以扩展,输出并出栈 ans[++pos] = arr[--top]; } else { // 否则继续搜索欧拉路径 dfs(arr[--top]); } } }
注意事项:有的让按照字典序,注意遍历顺序。有的建出来图是有重边的,注意ma[i][j]表示有连几次。
问题来了,这俩算法有个锤子不一样,甚至表面上复杂度都是o(m)都一样。我也不知道,反正以后就用前者吧,我没搞懂,看到有人说欧拉回路应用还不深入,所以知识点比较简单。
洛谷的这个要求字典序。
#include <bits/stdc++.h> #define ll long long #define ull unsigned long long #define met(a, b) memset(a, b, sizeof(a)) #define rep(i, a, b) for(int i = a; i <= b; i++) #define bep(i, a, b) for(int i = a; i >= b; i--) #define pb push_back #define mp make_pair #define debug cout << "KKK" << endl #define ls num*2 #define rs num*2+1 #define re return using namespace std; const ll mod = 1e9 + 7; const double PI = acos(-1); const ll INF = 2e18+1; const int inf = 1e9 + 15; const double eps = 1e-7; const int maxn = 1e5 + 5; int ma[257][257], fa[333], n = 256, du[333]; string ans; int f(int x){ if(x == fa[x]) return x; re fa[x] = f(fa[x]); } void dfs(int x){ // cout << x << endl; rep(i, 1, n){ if(ma[x][i]){ ma[x][i] = ma[i][x] = 0; dfs(i); } } ans += (char)x; } int main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int m; cin >> m; string s; rep(i, 1, n) fa[i] = i; int flag = 0, num = 0, st = 0; rep(i, 1, m){ cin >> s; ma[s[0]][s[1]] = ma[s[1]][s[0]] = 1; fa[f(fa[s[0]])] = f(fa[s[1]]); du[s[0]]++; du[s[1]]++; } st = 0; rep(i, 1, n){ if(fa[i] == i && du[i]) flag++; if(du[i] % 2 == 1){ num++; if(!st) st = i; } } if(flag != 1 || (num && num != 2)){ cout << "No Solution" << endl; re 0; } if(!st) rep(i, 1, n) if(du[i]){st = i; break;} dfs(st); reverse(ans.begin(), ans.end()); cout << ans << endl; re 0; }
hihoCoder #1181 : 欧拉路·二 有重边
#include <bits/stdc++.h> #define ll long long #define ull unsigned long long #define met(a, b) memset(a, b, sizeof(a)) #define rep(i, a, b) for(int i = a; i <= b; i++) #define bep(i, a, b) for(int i = a; i >= b; i--) #define pb push_back #define mp make_pair #define debug cout << "KKK" << endl #define ls num*2 #define rs num*2+1 #define re return using namespace std; const ll mod = 1e9 + 7; const double PI = acos(-1); const ll INF = 2e18+1; const int inf = 1e9 + 15; const double eps = 1e-7; const int maxn = 1e5 + 5; map<int, int> ma[1005]; vector<int> v[maxn]; int n, arr[maxn], ans[maxn], pos, top, du[maxn]; void dfs(int x){ arr[top++] = x; for(auto i: v[x]) if(ma[x][i] == 1){ ma[x][i] = 0; ma[i][x] = 0; dfs(i); break; } } void fleury(int ss) { int brige; top = 0; arr[top++] = ss; // 将起点放入Euler路径中 while (top > 0) { brige = 1; rep(i, 1, n) { // 试图搜索一条边不是割边(桥) if (ma[arr[top-1]][i]) { brige = 0; break; } } if (brige) { // 如果没有点可以扩展,输出并出栈 ans[++pos] = arr[--top]; } else { // 否则继续搜索欧拉路径 dfs(arr[--top]); } } } int main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int m; cin >> n >> m; int flag = 0, num = 0, st = 0; rep(i, 1, m){ int x, y; cin >> x >> y; if(ma[x][y] == 0)v[x].pb(y); v[y].pb(x); ma[x][y]++; ma[y][x]++; du[x]++; du[y]++; } st = 1; rep(i, 1, n){ if(du[i] % 2 == 1){ st = i; break; } } fleury(st); rep(i, 1, pos){ if(i == pos) cout << ans[i] << endl; else cout << ans[i] << ' '; } re 0; }