【图论】欧拉回路和欧拉通路

定义

下面讨论的图 (G) 均不存在孤立顶点(度数为0的顶点)。

欧拉回路和欧拉通路
通过图中所有边恰好一次的回路称为欧拉回路。
通过图中所有边恰好一次的通路称为欧拉通路。

欧拉图和半欧拉图
具有欧拉回路的无向图或有向图称为欧拉图。
具有欧拉通路但不具有欧拉回路的无向图或有向图称为半欧拉图。

若图 (G) 是欧拉图,则它为若干个边不重复的回路的并。
若图 (G) 是半欧拉图,则它为若干个边不重复的回路和一条简单路径的并。

欧拉图和半欧拉图判定的充要条件
对于无向图 (G)(G) 是欧拉图,当且仅当 (G) 是连通的,且没有度数为奇数的顶点。
对于无向图 (G)(G) 是半欧拉图,当且仅当 (G) 是连通的,且恰好有0个或2个度数为奇数的顶点。
对于有向图 (G)(G) 是欧拉图,当且仅当 (G) 的所有顶点属于同一个强连通分量,且每个顶点的入度和出度相同。
对于有向图 (G)(G) 是半欧拉图,当且仅当下列四个条件全部满足:
  若把有向边换成无向边, (G) 是连通的;
  且最多一个顶点的入度和出度差为1;
  且最多一个顶点的出度和入度差为1;
  除上述的两个顶点外,其他所有顶点的入度和出度相同。

代码

无向图欧拉回路

n       顶点数
m       无向边数
visv    在dfs1中,顶点i是否被遍历过(用于判断存在欧拉回路)
vise    在dfs2中,无向边i是否被遍历过(用于构造欧拉回路)
ans     构造欧拉回路的结果

initialize                  初始化图
addEdge                     向图中加边
existEulerianCircuit        判断存在欧拉回路,复杂度O(n)。
constructEulerianCircuit    构造欧拉回路,复杂度O(n+m)。
struct UndirectGraphEulerianCircuit {

    int n, m;
    vector<vector<pii>> G;

    vector<bool> visv;
    vector<bool> vise;
    vector<int> ans;

    void initialize(int n, int m) {
        this->n = n, this->m = m;
        G.clear();
        G.resize(n + 1);
        visv.clear();
        visv.resize(n + 1);
        vise.clear();
        vise.resize(m + 1);
    }

    void addEdge(int u, int v, int eid) {
        G[u].emplace_back(v, eid);
        G[v].emplace_back(u, eid);
    }

    void dfs1(int u) {
        visv[u] = 1;
        for (const pii &e : G[u]) {
            int v = e.first;
            if (!visv[v])
                dfs1(v);
        }
    }

    bool existEulerianCircuit() {
        for (int i = 1; i <= n; ++i) {
            if (G[i].size() & 1)
                return 0;
        }
        int comp = 0;
        for (int i = 1; i <= n; ++i) {
            if (G[i].size() && !visv[i]) {
                ++comp;
                if (comp >= 2)
                    return 0;
                dfs1(i);
            }
        }
        return 1;
    }

    void dfs2(int u, int peid) {
        while (G[u].size()) {
            pii e = G[u].back();
            G[u].pop_back();
            int v = e.first, eid = e.second;
            if (vise[eid])
                continue;
            vise[eid] = 1;
            dfs2(v, eid);
        }
        if (peid != -1)
            ans.emplace_back(peid);
    }

    void constructEulerianCircuit() {
        ans.clear();
        for (int i = 1; i <= n; ++i) {
            if (G[i].size()) {
                dfs2(i, -1);
                break;
            }
        }
        reverse(ans.begin(), ans.end());
    }

} G;

拓展

构造欧拉通路:先判断欧拉通路存在。若欧拉回路存在,找出欧拉回路输出。若欧拉通路存在,连接一条附加边使得欧拉回路存在,找出欧拉回路,然后把附加边删除。
构造字典序最小的欧拉回路:对每个点的出边排序,并且从字典序最小的起点开始,每一步贪心选择。
构造混合图欧拉回路:转化为网络流问题。

练习

CF1361C - Johnny and Megan's Necklace
CF1038E - Maximum Matching

原文地址:https://www.cnblogs.com/purinliang/p/14335332.html