首页 > 其他分享 >【图论】欧拉图

【图论】欧拉图

时间:2024-06-08 09:32:46浏览次数:22  
标签:图论 int dfs stk edge 回路 欧拉

欧拉回路Eulerian Cycle:通过图中每条边恰好一次的回路
欧拉通路Eulerian Path:通过图中每条边恰好一次的通路
欧拉图:具有欧拉回路的图
半欧拉图:具有欧拉通路但不具有欧拉回路的图

欧拉图中所有顶点的度数都是偶数。
若 G 是欧拉图,则它为若干个环的并,且每条边被包含在奇数个环内。

判别法

无向图是欧拉图当且仅当:
非零度顶点是连通的
顶点的度数都是偶数
这个条件很容易满足。

无向图是半欧拉图当且仅当:
非零度顶点是连通的
恰有2个奇度顶点
这个条件很容易满足。

有向图是欧拉图当且仅当:
非零度顶点是强连通的
每个顶点的入度和出度相等
(强连通 = 从同一个强连通的每个点出发都能到达分量上的所有点)

有向图是半欧拉图当且仅当:
非零度顶点是弱连通的
至多一个顶点的出度与入度之差为1
至多一个顶点的入度与出度之差为1
其他顶点的入度和出度相等

关于零度顶点连通与否,是否属于欧拉图的问题,看每个问题具体的定义。

Hierholzer 算法

过程
算法流程为从一条回路开始,每次任取一条目前回路中的点,将其替换为一条简单回路,以此寻找到一条欧拉回路。如果从路开始的话,就可以寻找到一条欧拉路。(或者先把缺失的那条边补上,找到一个欧拉回路,再从缺失的边那里剪开,即可。)首先要确定要求的欧拉回路或者欧拉通路存在,如果是欧拉回路存在可以从任何点开始dfs,如果只有欧拉通路存在,则要在无向图的奇数度数点开始dfs,有向图在唯一的出度比入度大1的点开始dfs。dfs完成后栈中的元素逆序就是一条欧拉回路/欧拉通路。为了保证复杂度,要在算法的过程中跳过已经遍历过的边,这个问题是这个算法最让人头疼的地方,最简单的方式是使用set,但是会涉及到在for迭代中删除set中元素的问题。

这里一般用set来存图,然后实现删边,从而使得算法的复杂度为 \(O(m\log{m})\) ,注意,stl中提供erase的容器,返回值为删除之后的下一个迭代器。也就是说,如果删除成功,则直接用erase的返回值就可以继续循环,否则直接迭代器++即可。

这个是C++ 11的标准写法:

for (auto it = my_set.begin(); it != my_set.end(); ) {
    if (some_condition) {
        it = numbers.erase(it);
    } else {
        ++it;
    }
}

对于有向图,下面的算法是有效的(仅限无平行边的简单情况,不推荐):

set<int> G[MAXN];
stack<int> stk;
 
void dfs (int u) {
    for (auto it = G[u].begin(); it != G[u].end(); ) {
        int v = *it;
        it = G[u].erase(it);
        dfs(v);
    }
    stk.push (u);
}

先判断欧拉通路存在(最多只有一对节点入度和出度不相等,并且一个是大一一个是小一)。只需要在出度唯一比入度大1的节点开始dfs,或者如果没有这样的节点,从任意节点开始dfs,然后把stk中的节点逆序输出,如果stk中的节点数量恰好等于边数+1,则欧拉通路/欧拉回路已经被找到,否则此图不连通,无解。

但是如果是无向图,这个算法要删除别的地方的set循环中的迭代器,又引入了新的不确定。

从luogu https://www.luogu.com.cn/article/kv9n9167 这篇文章学到了一个很好的方式。

使用链式前向星的方法存图,相邻的边要同时加入。然后每个节点维护一个链表的头节点,当在dfs到u,然后删除u中指向v的边时,容易知道v一定是此时链表的头节点,此时把头节点向前移动1,如果下一次再遇到u节点则不会再遍历同一个头。同时将(u,v)这一条边打上已被使用的标记。当遍历节点v时,如果遇到了指向u的那条边(反向边),那么检查这个反向边是否已经被遍历,如果是的话则继续移动头节点。这个也是链式前向星写法无法被vector写法替代的一个地方。

namespace EulerianPath {
int n;
struct Edge {
    int u;
    int v;
    int next;
};
vector<Edge> edge;
vector<int> head;

void Init (int _n) {
    n = _n;
    edge.clear();
    edge.push_back ({0, 0, 0});
    head.clear();
    head.resize (n + 1);
}

void AddDirectedEdge (int u, int v) {
    Edge e = {u, v, head[u]};
    head[u] = (int) edge.size();
    edge.push_back (e);
}

int HaveEulerianCycle() {
    vector<int> outdeg (n + 1);
    vector<int> indeg (n + 1);
    for (auto [u, v, next] : edge) {
        ++outdeg[u];
        ++indeg[v];
    }
    for (int i = 1; i <= n; ++i) {
        if (outdeg[i] != indeg[i]) {
            return -1;
        }
    }
    // 如果连通
    return 1;
}

int HaveEulerianPath() {
    vector<int> outdeg (n + 1);
    vector<int> indeg (n + 1);
    for (auto [u, v, next] : edge) {
        ++outdeg[u];
        ++indeg[v];
    }
    int inV = 0, outV = 0;
    for (int i = 1; i <= n; ++i) {
        if (outdeg[i] == indeg[i]) {
            continue;
        }
        if (outdeg[i] > indeg[i]) {
            if (outV == 0) {
                outV = i;
            } else {
                return -1;
            }
        } else {
            if (inV == 0) {
                inV = i;
            } else {
                return -1;
            }
        }
    }
    // 如果连通
    return outV ? outV : 1;
}

vector<int> FindEulerCyclerOrPath() {
    int S = HaveEulerianPath();
    if (S == -1) {
        return {};
    }
    vector<int> stk;
    auto dfs = [&] (auto self, int u) {
        for (int &i = head[u]; i;) {
            auto [_u, v, next] = edge[i];
            i = next;
            dfs (v);
        }
        stk.push (u);
    }
    if (stk.size() == edge.size()) {
        // 因为edge有一条编号为0的虚拟边,所以大小刚好相等
        reverse (stk.begin(), stk.end());
        return stk;
    }
    return {};
}

}

上述算法未经验证

标签:图论,int,dfs,stk,edge,回路,欧拉
From: https://www.cnblogs.com/purinliang/p/18238135

相关文章

  • 【数据结构】图论入门
    引入数据的逻辑结构:集合:数据元素间除“同属于一个集合”外,无其他关系线性结构:一个对多个,例如:线性表、栈、队列树形结构:一个对多个,例如:树图形结构:多个对多个,例如:图图的基本概念及术语图:G=(V,E)V:顶点(数据元素)的有穷非空集合E:边的有穷集合图的类型定义无向图:每......
  • Codeforces Round 949 (Div. 2)D. Turtle and Multiplication(欧拉路径、线性筛、思维
    Problem-D-Codeforces  按照官方正解做即可,顺带存个jiangly板子。1#include<bits/stdc++.h>23usingi64=longlong;4std::vector<int>minp,primes;56voidsieve(intn){7minp.assign(n+1,0);8primes.clear();910......
  • §3. 欧拉积分
    了解欧拉积分的定义和其他形式,掌握他们的性质,主要是伽马函数的递推公式,贝塔函数的对称性和递推公式,以及贝塔函数和伽马函数的关系。难点:利用欧拉积分求定积分。重点习题:习题1-3   莱昂哈德·欧拉(LeonhardEuler,1707年4月15日~1783年9月18日),瑞士数学家,13岁进巴塞尔大学读......
  • 图论-SPFA与差分约束
    闻道有先后,术业有专攻当用来判断负环的时候,SPFA还挺好用的intpre[N];voidprint_path(ints,intt){if(s==t){cout<<s;return;}print_path(s,pre[t]);cout<<""<<t;}inthead[N],cnt;structEdge{intfrom,to,nxt,c;}e[......
  • 图论
    1图论1.1图的建立1.1.1领接表边权建图importjava.util.ArrayList;importjava.util.List;importjava.util.Scanner;publicclassMain{//定义图的邻接表表示staticList<int[]>[]g;//节点数staticintn;//保存某种状态或结果的数组......
  • 欧拉回路
    概念1.经过图中所有边恰好一次的通路称为欧拉通路或欧拉路(起点终点可以不一致)2.经过图中所有边恰好一次的回路称为欧拉回路(起点终点一致)3.判别方法:对于无向图G,G中存在欧拉回路当且仅当G中所有度非0的点是连通的且没有奇数度数的点对于无向图G,G中存在欧拉路当且仅当G中......
  • 最短路图论
    dijkstraCode:#include<bits/stdc++.h>usingnamespacestd;typedefpair<int,int>pii;constintN=1e5+5,inf=INT_MAX;intn,m,dis[N],s;//structnode{//intfrom,to,w,val;//};boolvis[N];vector<pii>edge......
  • 欧拉Openeuler安装k8s1.24.6
    一、环境[root@tax-k8s-work03~]#cat/etc/os-releaseNAME="HuaweiCloudEulerOS"VERSION="2.0(x86_64)"ID="hce"VERSION_ID="2.0"PRETTY_NAME="HuaweiCloudEulerOS2.0(x86_64)"ANSI_COLOR="0;31"[r......
  • 知识点整理 - 连通性相关 | 《综述图论中连通性及相关问题的一些处理方法》学习笔记
    是ix35老师论文的学习笔记,同时也用作连通性相关知识梳理。可能不会包含很多定义,只会挑重要的写。会包含一些例题。定义与记号\(u\rightsquigarrowv\)代表\(u\)到\(v\)的一条路径。有时也会用这个记号表示连通性。无向图点/边连通度:若\(u,v\)任意点割集大小不小......
  • 图论-最近公共祖先
    例题:祖孙询问 给定一棵包含 n......