欧拉回路路径求解

2023-11-06

基本概念:
今天讨论的主题是一类问题,就是欧拉路问题。有两种欧拉路。第一种叫做 Eulerian path(trail),沿着这条路径走能够走遍图中每一条边;第二种叫做 Eularian cycle,沿着这条路径走,不仅能走遍图中每一条边,而且起点和终点都是同一个顶点。注意:欧拉路要求每条边只能走一次,但是对顶点经过的次数没有限制。

满足什么性质的图才能有欧拉路?根据 wikipedia 对欧拉路的介绍:

在无向图中,所有顶点的度数均为偶,则存在 Eularian cycle;若有且仅有两个顶点的度数为奇,其余的都为偶,则存在 Eularian path;
在有向图中,所有顶点的入度数等于出度数,则存在 Eularian cycle;若有且仅有两个顶点:其中一个入度数比出度数大 1,另一个入度数比出度数小 1,其余的顶点入度数等于出度数,则存在 Eularian path.
另外我们还需要知道,对于那些 Eularian path,起点和终点分别在那两个度数为奇的顶点上(对于无向图)或是入度数不等于出度数的顶点上(对于有向图)。

Fleury算法:
然而知道这些并没有给我们带来多少实惠。因为我们除了判定一个图有没有欧拉路之外,更想找到其中的一条欧拉路径。于是这就是我们今天的重点:寻找欧拉路径的算法。
Fleury算法的思想是:能不走桥就尽量不走桥。(桥:在未被走过的边集中,如果去掉某条边使得剩下的图不连通,则称这条边为桥。)
一个比较经典的算法是 Fleury 算法。Fleury 算法的思想就是:在过河拆桥之前,先想想有没有退路。为什么这么说?Fleury 算法每个回合进行到一个顶点上的时候,都会删除已经走过的边。在选择下一条边的时候,不应该出现这样的状况:在删除下一条边之后,连通图被分割成两个不连通的图。除非没有别的边可选择。该算法从一个奇度数顶点开始(若所有顶点度数均为奇,则任选一个顶点)。当所有的边都走完的时候,该算法结束,欧拉路径为删除路径的顺序。用算法伪代码描述就是:

v_0 <- a vertex with odd degree or, if no such vertex, any arbitrary vertex.
Repeat:
    select an vertex v_i+1 adjacent of v_i, which should not separate the graph or, the only adjacent vertex of v_i
    remove edge <v_i, v_i+1> and jump to v_i+1
Until all edges have been visited.
Return the sequence of visited edges.

但是该算法的问题就是,怎么判断一条边是否是一个桥呢?如果使用 Tarjan 算法判断,则算法运行时间就是 O(E2) 。在实际写代码的时候,我可没考虑那么多。我只考虑,如果在某一点处深搜的结果导致图被分离,那么在某一个边必然走过了一个桥,那么就返回走另一条边。这样的思想形成的算法如下:

include <cstdio>
#include <stack>
#include <iostream>
#include <string>

using namespace std;

int G[1001][1001];
int N,M;
stack<int> S;

bool dfs(int u){ //返回的状态说明是否走过了一个桥
    S.push(u); //进入某一节点时推入节点,如果误入歧途还要负责弹出。
    if(G[u][0]==0){ //G[u][0]代表该节点所邻接的边的数目。此段代码判断是否走完了所有边,或者没有走完。
        bool flag=true;
        for(int i=1; i<=N; i++){
            if (i==u) continue;
            flag=((G[i][0]==0) && flag);
        }
        if(flag==false){
            S.pop();
        }
        return flag;
    }

    for(int v=1; v<=N; v++)
        if(G[u][v]){
            //删除边
            G[u][v]-=1;
            G[v][u]-=1;
            G[v][0]-=1;
            G[u][0]-=1;
            if(dfs(v)) return true;
            else{//撤销删除边
                G[u][v]+=1;
                G[v][u]+=1;
                G[v][0]+=1;
                G[u][0]+=1;
            }
        }

    S.pop();
    return false;
}


int main(){
    freopen("testcase", "r", stdin);
    cin>>N>>M;
    int u,v;
    for(int i=0; i!=M; i++){
        cin>>u>>v;
        G[u][v]+=1;
        G[v][u]+=1;
        G[u][0]+=1;
        G[v][0]+=1;
    }
    //寻找起点
    for(u=1; u<=N; u++){
        if(G[u][0]&1) break;
    }
    if(u==N+1) dfs(1);
    else dfs(u);
    while(!S.empty()){
        cout<<S.top()<<" ";
        S.pop();
    }
    cout<<endl;
    return 0;
}

粗略分析一下,由于算法要经过每条边,所以时间必然是 Ω(E) 。在最坏情况下,在每个节点处进行一次 DFS,节点会重复走所以以边计算,所以算法复杂度应该是 O(E(E+V))

Hierholzer 算法:
另一种计算欧拉路的算法是 Hierholzer 算法。这种算法是基于这样的观察:
这里写图片描述
在手动寻找欧拉路的时候,我们从点 4 开始,一笔划到达了点 5,形成路径 4-5-2-3-6-5。此时我们把这条路径去掉,则剩下三条边,2-4-1-2 可以一笔画出。

这两条路径在点 2 有交接处(其实点 4 也是一样的)。那么我们可以在一笔画出红色轨迹到达点 2 的时候,一笔画出黄色轨迹,再回到点 2,把剩下的红色轨迹画完。

由于明显的出栈入栈过程,这个算法可以用 DFS 来描述。
如果想看得更仔细一点,下面是从点 4 开始到点 5 结束的 DFS 过程,其中 + 代表入栈,- 代表出栈。
4+ 5+ 2+ 3+ 6+ 5+ 5- 6- 3- 1+ 4+ 2+ 2- 4- 1- 2- 5- 4-
我们把所有出栈的记录连接起来,得到
5-6-3-2-4-1-2-5-4

诸位看官可以自己再选一条路径尝试一下。不过需要注意的是,起始点的选择和 Fleury 要求的一样。
这个算法明显要比 Fleury 高效,它不用判断每条边是否是一个桥。我写的代码如下:

include <cstdio>
#include <stack>
#include <iostream>
#include <string>
#include <vector>

using namespace std;

int G[1001][1001];
int N,M;
stack<int> S;

void dfs(int u){
    for(int v=1; v<=N; v++)
        if(G[u][v]){
            G[u][v]-=1;
            G[v][u]-=1;
            dfs(v);
            //不用恢复边!
        }
    S.push(u);//出栈时记录
}


int main(){
    freopen("testcase", "r", stdin);
    cin>>N>>M;
    int u,v;
    vector<int> cnt(N+1,0);
    for(int i=0; i!=M; i++){
        cin>>u>>v;
        G[u][v]+=1;
        G[v][u]+=1;
        cnt[u]^=1;//利用了异或运算,0表示度为偶数,1表示度为奇数。
        cnt[v]^=1;
    }
    for(u=1; u<=N; u++){
        if(cnt[u]) break;
    }
    if(u==N+1) dfs(1);
    else dfs(u);
    while(!S.empty()){
        cout<<S.top()<<" ";
        S.pop();
    }
    cout<<endl;
    return 0;
}

需要注意的是这个算法时间复杂度是 O(E) 。其在 DFS 的过程中不用恢复边,靠出栈记录轨迹。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

欧拉回路路径求解 的相关文章

  • Road Construction POJ - 3352(tarjan双连通缩点模板)

    题目描述 给一个无向连通图 至少添加几条边使得去掉图中任意一条边不改变图的连通性 即使得它变为边双连通图 include
  • Codeforces-1454E Number of Simple Paths(基环树-思维)

    题目大意 给你n个点 n条边 求图中简单路径的个数 题目思路 n个点n条边 那么图中一定有一个环 拿这个图来讲 我们将两点间的关系分为4种 1 两点都在环上 简单路径的个数为2 例如2与5 2 一个点在环上一个点不在环上 简单路径个数为2
  • 22年菲尔兹奖获得者HUGO DUMINIL-COPIN 研究内容总结

    雨果 迪米尼 科潘获得 2022 的菲尔兹数学奖 因解决了统计物理中相变 概率理论的长期问题 特别是三维和四维问题以及二维中的不可积情况 下 雨果最显著的成果是三维和四维 Ising 模型 他与合作者解决了 80 年代以来一直存在的问题 建
  • [UVA1364

    评测地址 网址1 网址2 题目描述 题意 给出n位骑士 然后有m个关系 每个关系以格式 a b a b a b给出 表达骑士 a a
  • 《算法图解》读书笔记(二)

    第六章 图 广度优先搜索 1 解决最短路径问题 shortest path problem 的算法被称为广度优先搜索 breadth first search 2 图由节点 node 和边 edge 组成 一个节点可能与众多节点直接相连 这
  • 每天进步一点点【图的深度优先搜索与广度优先搜索】

    图是一种数据结构 其中节点可以具有零个或多个相邻元素 两个节点之间的连接称为边 节点也可以称为顶点 图分为三种 无向图 有向图 带权图 图的表示方式有两种 二维数组表示 邻接矩阵 链表表示 邻接表 邻接矩阵 邻接矩阵是表示图形中顶点之间相邻
  • 【算法】深度优先搜索DFS 入门:基本知识+经典例题

    DFS最重要的是理清搜索顺序 ps 这是我入门dfs时写的博客 后来dfs渐渐熟练了 也补充了一些题目上去 带原题和代码 个人感觉整篇博文从上到下确实由易到难 代码也由开始的冗长变得渐渐精简 自学DFS看的视频 小甲鱼 讲原理 青岛大学 王
  • Codeforces Round #751 (Div. 2) D. Frog Traveler(BFS)

    题解 因为我们最多把所有的点跳一遍么 所以直接BFS模拟一下就行了 注意现在跳的点不能是以前已经跳过的点 并且只能越跳越高 否则没有意义 这样就保证了时间复杂度是线性的 AC代码 include
  • UVA-10603 倒水问题 题解答案代码 算法竞赛入门经典第二版

    GitHub jzplp aoapc UVA Answer 算法竞赛入门经典 例题和习题答案 刘汝佳 第二版 使用广度优先搜索和优先队列 如果找到最小的点则退出 找不到就遍历所有的情况 include
  • 数据结构——图的DFS(深度优先遍历)- C语言代码实现

    图的深度优先遍历的基本思想 从图中某顶点v出发 1 访问顶点v 2 依次从v的未被访问的邻接点出发 对图进行深度优先遍历 直至图中和v有路径相通的顶点都被访问 3 若此时图中尚有顶点未被访问 则从一个未被访问的顶点出发 重新进行深度优先遍历
  • hdu1827Summer Holiday【tarjan强连通分量解决最小联系费用】

    1A 撒花 这比买买买开心多了 思路 既然是强连通分量的题 很容易想到形成的东西是一坨一坨的 哈哈 然后如果某一坨入度为0 那么很不幸 这一坨只能直接被威士忌通知 至于具体通知这一坨中的哪一个 枚举一遍就知道了 最后把话费求和 感觉强连通分
  • 2023杭电暑假多校4 题解

    3 Simple Set Problem 题意 k 个多重集合 每个集合选出一个数形成新集合A 求 m a x A m
  • Dinic算法学至大佬,学以致用【挂上相应的题目】

    这个巨佬讲的超级厉害 学起来很快 还有优化的说呢 Dinic算法 研究总结 网络流 网络流是信息学竞赛中的常见类型 笔者刚学习了最大流Dinic算法 简单记录一下 网络流基本概念 什么是网络流 在一个有向图上选择一个源点 一个汇点 每一条边
  • 图论--最近公共祖先LCA

    最近公共祖先LCA LCA Least Common Ancestors 即最近公共祖先 是指这样一个问题 在有根树中 找出某两个结点u和v最近的公共祖先 另一种说法 离树根最远的公共祖先 最近公共祖先是相对于两个节点来说的 一般来说 最近
  • 迷宫问题java老鼠走迷宫(回溯法,递归,二维数组)(超级容易理解)

    回溯法迷宫问题 思路 利用回溯法和递归思想解决 findWay 方法就是专门来找出迷宫的路径 如果找到 就返回 true 否则返回 false map 就是二维数组 即表示迷宫 i j 就是老鼠的位置 初始化的位置为 1 1 因为我们是递归
  • The Necklace 【UVA - 10054】【欧拉回路详解】

    题目链接1 题目链接2 题目求的是一串珠子 要让它们首尾相互照应才能串起来 并且 最后要连成一个环 使得最后的珠子的尾与第一个珠子的头相互对应 那么 这道题就是道求欧拉回路的题了 我们要先判断这是不是能够构成欧拉回路 这是个无向图 再对于需
  • BFS遍历树和DFS遍历树

    遍历树 按照遍历的顺序 如不清楚图的遍历 请先阅读图的遍历 绘制成树型结构 DFS遍历树 以下为图到遍历树的转化 如果不清楚图的遍历 请先阅读笔者的另一篇文章 图的遍历 动图 按照DFS遍历的顺序 绘制成一棵树 途中红色的边就是遍历过程中没
  • 有向图的拓扑排序

    给定一个 nn 个点 mm 条边的有向图 点的编号是 11 到 nn 图中可能存在重边和自环 请输出任意一个该有向图的拓扑序列 如果拓扑序列不存在 则输出 1 1 若一个由图中所有点构成的序列 AA 满足 对于图中的每条边 x y x y
  • 讲解 最大流问题+最小花费问题+python(ortool库)实现

    文章目录 基本概念 图 邻接矩阵 最大流问题 python解决最大流问题 python解决最大流最小费用问题 喜欢的话请关注我们的微信公众号 你好世界炼丹师 公众号主要讲统计学 数据科学 机器学习 深度学习 以及一些参加Kaggle竞赛的经
  • 数模培训第二周——图论模型

    图论中最短路算法与程序实现 图论中的最短路问题 包括无向图和有向图 是一个基本且常见的问题 主要的算法有Dijkstra算法和Floyd算法 Floyd算法 简介 Floyd Warshall算法 英语 Floyd Warshall alg

随机推荐

  • FRP代理及其在数据库安全上的实践

    1 代理 现如今的互联网世界里 代理服务已经十分常见 它通常作为一个第三方或者说中转站角色替代用户取得信息或者服务 根据代理对象的不同 代理服务可以分为正向代理和反向代理 1 1 正向代理 我们通常所说的代理一般都指的是正向代理 正向代理的
  • LlamaIndex 提供的索引

    LlamaIndex 以前称为 GPT Index 是一个开源项目 它在 LLM 和外部数据源 如 API PDF SQL 等 之间提供一个简单的接口进行交互 它提了供结构化和非结构化数据的索引 有助于抽象出数据源之间的差异 它可以存储提示
  • 严重漏洞已存在16年,数亿台打印机受影响

    聚焦源代码安全 网罗国内外最新资讯 编译 奇安信代码卫士 安全专家在惠普 Xerox 和三星使用的常用打印机驱动中找到一个严重漏洞 该漏洞编号为 CVE 201 3438 在2005年就存在于打印机驱动代码中 影响过去16年来出售的数亿台打
  • 火狐浏览器使用HttpRequester模拟发送http请求

    个人感觉网上的在线模拟http请求工具相比火狐的HttpRequester没那么实用方便 首先安装HttpRequester 打开菜单 附加组件 搜索框输入 HttpRequester 就会搜出来 然后点击安装 成功后在工具这里就会显示有H
  • Android数据的四种存储方式

    Android数据的四种存储方式 SharePreferences SQLite Contert Provider File 网络存储 作为一个完整的应用程序 数据存储的操作是必不可少的 Android系统提供了四种存储数据方式 分别为 S
  • Cache-Control浏览器缓存

    描述 同一个标签页 打开 A 站点 访问 config 接口 正常 打开 B 站点 访问 config 接口 正常 通过浏览器后退返回 A 站点 访问 config 接口 数据异常 config 返回了 B 站点的数据 测试站点数据 htt
  • 浅谈Spring切面编程AOP的实现,以及两种写法aspect和advisor

    在开发过程种 很多地方会用到Spring的AOP 之前一致使用
  • 在centos7系统下解决java程序需要依赖外部jar包的问题

    javac cp xxxx jar bbbbb java cp也可以换成 classpath 转载 https blog csdn net weixin 34023863 article details 94448291 utm mediu
  • LVS+Keepalived群集

    Keeoalived 工具介绍 专门为LVS 和 HA 设计的一款健康检查工具 专为LVS和HA设计的一款健康检查工具 支持故障自动切换 Failover 以及节点健康状态检查 Health Checking 判断LVS负载调度器 节点服务
  • cairo[00] Windows环境学习笔记——Visual Studio配置GTK

    GTK是什么 GTK是一个跨平台的前端 在本笔记中 只会用作呈现 实际绘图依然由cairo完成 配置GTK 我们可以直接通过终端来将GTK直接加载进Visual Studio的内核 这样每次新建任务就不用再进行配置 前提是你安装了Visua
  • 【C++从入门到放弃】C++/g++不同文件夹的编译

    本文大面积参考了简书资料 https www jianshu com p 2b047bcce8fa 由于源书上存在好几处细节上的问题 比如 class Afunc 应该是 class A std cout lt lt include A l
  • Docker + ELK + SpringBoot部署

    步骤一 Docker安装ELK镜像 docker pull sebp elk 步骤二 启动镜像 方法一 Docker部署 docker run e ES JAVA OPTS Xms256m Xmx256m 设置虚拟机所需内存大小 p 560
  • 面试题:100个白球,100个黑球,每次取两个

    面试题 袋子里有黑白球各100个 每次从袋子里取2个球 若取的球颜色相同 则放入1个黑球 若不同 则放入1个白球 问 最后袋子里剩下1个黑球的概率是多少 思路一 每次取球有3种情况 1 两黑 此时放入1个黑球 黑球在袋子里个数为奇数个 白球
  • 网络原理详解(图文结合)

    目录 编辑一 应用层 1 请求和响应 2 通用的协议格式 1 xml 2 json 3 protobuffer 二 传输层 1 UDP 2 TCP 1 TCP 协议段格式 2 可靠传输的实现机制 确认应答机制 可靠性 超时重传机制 可靠性
  • VMware 安装 Windows11

    一 下载win11的镜像文件 可以在MSDN网站下载 MSDN系统库 致力于原版windows生态服务 二 创建虚拟机 1 双击打开VMware虚拟机 点击创建新的虚拟机 2 选择典型 推荐 然后点击下一步 3 在安装程序光盘映像文件下 点
  • 基于无法安装64位版本的visio,因为在您的PC上找到了以下32位程序的解决办法

    今天在给自己安装visio 64位版本的时候 出现 无法安装64位版本的visio 为在您的PC上找到了以下32位程序 microsoft access database engine 2010 english 请卸载所有32位Office
  • JAVA全排列算法

    利用java知识编写全排列算法 里面有我的个人理解注释 代码如下 package demo import java util ArrayList import java util Arrays import java util List S
  • 夺灵者哈卡(Hakkar, the Soulflayer)

    Hakkar the Soulflayer夺灵者哈卡Deathrattle Shuffle a Corrupted Blood into each player s deck 亡语 将一张 堕落之血 分别洗入双方玩家的牌库 Corrupte
  • java main()线程是不是最后一个退出的(相比较main中创建的其他多个线程)

    JVM会在所有的非守护线程 用户线程 执行完毕后退出 main线程是用户线程 仅有main线程一个用户线程执行完毕 不能决定JVM是否退出 也即是说main线程并不一定是最后一个退出的线程 public class MainThreadTe
  • 欧拉回路路径求解

    基本概念 今天讨论的主题是一类问题 就是欧拉路问题 有两种欧拉路 第一种叫做 Eulerian path trail 沿着这条路径走能够走遍图中每一条边 第二种叫做 Eularian cycle 沿着这条路径走 不仅能走遍图中每一条边 而且