欧拉回路

欧拉回路(一笔画问题)

无向图

度数为奇数的点有0个或2个,0个就是一个欧拉回路,2个就是一个欧拉路径(欧拉回路也是欧拉路径)

有向图

  • 欧拉路径:要么所有的点的入度出度,要么除了两个点以外,其余的点的入度出度,剩余的两个点一个满足入度1(起点),一个满足出度1(终点)

  • 欧拉回路:所有点的入度==出度

求法:

环可以合并进边, 或者环可以和环合并(类似于绕八字)

对于除了起点和终点之外的点由于他们的度数都是偶数,因此只要从某一个环的出度出发,就一定会走完这个环回到这个点,因此第一次走到头一定是走到了终点

合并环的方法:有环先走环

如果所有的环都集中一个点,那么每次遍历到这个点,都会走m次边,因此复杂度最坏就是$O(m^2)$,所以优化一下

有向图:每用一条边 删掉

无向图:每用一条边标记对应的反向

如果把所有的环都遍历过了,最后遍历到的终点,直接回溯

欧拉回路模板

[link](1184. 欧拉回路 - AcWing题库)

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <unordered_map>
#include <cmath> 
#include <stack>
#include <iomanip>
#include <deque> 
#include <sstream>
#define x first
#define y second
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
bool ok[N];
int res[M], cnt, din[N], dout[N];
int type;
void dfs(int u) {
    for (int &i = h[u]; ~i;) {
        if (ok[i]) { // 保证这个点用过的边都被删去,否则点会被用很多次,剪枝
            i = ne[i];
            continue;
        }
        ok[i] = true; // 表示这个边被遍历了
        if (type == 1) ok[i ^ 1] = true; // 小trik无向边两个一定是相邻的,奇偶分布,这样可以快速找到另一个边
        int t;
        if (type == 1) { // 01 23 45 
            t = i / 2 + 1; // 找到对应的边的编号 编号从一开始
            if (i & 1) t = -t; // 往回走的是负            
        } 
        else t = i + 1;
        int j = e[i];
        i = ne[i]; // 往下一条边
        dfs(j); // 回溯回来表示这条边搜的环的加u人了
        res[++ cnt] = t; // 将这条边加进去
    }
}
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    memset(h, -1, sizeof h);
    cin >> type >> n >> m;
    for (int i = 0; i < m; i ++ ) {
        int a, b;
        cin >> a >> b;
        add(a, b);
        if (type == 1) add(b, a);
        din[b] ++, dout[a] ++;
    }
    if (type == 1) {
        for (int i = 1; i <= n; i ++ )
            if (din[i] + dout[i] & 1) {
                cout << "NO" << endl;
                return 0;
            }
    }
    else {
        for (int i = 1; i<= n; i ++ ) 
            if (din[i] != dout[i]) {
                cout << "NO" << endl;
                return 0;
            }
    }
    for (int i = 1; i <= n; i ++ )  
        if (h[i] != -1) { // 找一个起点
            dfs(i);
            break;
        }
    if (cnt < m) {
        cout << "NO" << endl;
        return 0;
    }
    cout << "YES" << endl;
    for (int i = cnt; i; i -- ) cout << res[i] << ' ';
    cout << endl;
    return 0;
}
posted @ 2022-03-31 17:11  枉玊  阅读(139)  评论(0)    收藏  举报