首页 > 其他分享 >学习笔记:欧拉图 & 欧拉路

学习笔记:欧拉图 & 欧拉路

时间:2023-10-28 16:12:52浏览次数:28  
标签:int 路径 dfs 学习 笔记 顶点 now 欧拉

欧拉图 & 欧拉路

定义

图中经过所有边恰好一次的路径叫欧拉路径(也就是一笔画)。如果此路径的起点终点相同,则称其为一条欧拉回路

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

性质

欧拉图中所有顶点的度数都是偶数。

若 \(G\) 是欧拉图,则它为若干个环的并,且每条边被包含在奇数个环内。

判别法

  1. 无向图是欧拉图当且仅当:
    • 非零度顶点是连通的。
    • 顶点的度数都是偶数。
  2. 无向图是半欧拉图当且仅当:
    • 非零度顶点是连通的。
    • 恰有 2 个奇度顶点。
  3. 有向图是欧拉图当且仅当:
    • 非零度顶点是强连通的。
    • 每个顶点的入度和出度相等。
  4. 有向图是半欧拉图当且仅当:
    • 非零度顶点是弱连通的
    • 至多一个顶点的出度与入度之差为 1。
    • 至多一个顶点的入度与出度之差为 1。
    • 其他顶点的入度和出度相等。

当然,一副图有欧拉路径,还必须满足将它的有向边视为无向边后它是连通的(不考虑度为 \(0\) 的孤立点),连通性的判断我们可以使用并查集dfs 等。

存在欧拉回路(即满足存在欧拉回路的条件),也一定存在欧拉路径。

求欧拉路径或欧拉回路

寻找欧拉路径(默认存在)

首先根据题意以及判定先确定起点 \(S\)。
从起点 \(S\) 开始 dfs

dfs 伪代码如下:

void dfs(int now){
    枚举 now 的出边。
        如果该边还未被访问
            标记为已访问
            dfs(该边连向的另一个点)
    now入栈
}

最后倒序输出栈内的所有节点即可。

感性理解倒序输出的原因:如果是欧拉回路,那么遍历到哪,输出到哪也是对的,因为不管怎么走都会绕某个环走回起点,所以不到最后不会出栈,然而欧拉路径会出现边都被走过了,走不回起点,最后会停留在终点,遇到这种情况这种路径会最先出栈,于是只要把这个路径先走了,前面就和欧拉回路一样随便走就行,不会出栈,于是倒序输出就是对的。

例题

洛谷 P7771 【模板】欧拉路径

题目描述

求有向图字典序最小的欧拉路径。

输入格式

第一行两个整数 \(n,m\) 表示有向图的点数和边数。

接下来 \(m\) 行每行两个整数 \(u,v\) 表示存在一条 \(u\to v\) 的有向边。

输出格式

如果不存在欧拉路径,输出一行 No

否则输出一行 \(m+1\) 个数字,表示字典序最小的欧拉路径。

题意:给定 \(n\) 个点,\(m\) 条边,求这副有向图字典序最小的欧拉路径。

思路

本题需要判断并找出有向图的欧拉路径。

由于本题保证“将有向边视为无向边后图连通”,所以判定时不用判断连通性。

还有一点要注意的是本题需要按照字典序输出。

这一点如何解决呢?

法一:

直接使用数组存邻接矩阵,枚举点 \(x\) 出边时,直接枚举编号从 \(1\) 到 \(n\) 的点 \(y\),再判断 \(x\),\(y\) 之间是否有未访问边,这样就解决了字典序的问题。

dfs 代码(对应伪代码):

void dfs(int now){
  for(int i = 1 ; i <= n ; i ++)
      if(G[u][i] > 0)
          G[u][i]--,dfs(i);
  st.push(now);
}

但是这样的做法时间复杂度为 \(O(n^2)\),显然会超时。

法二:

既然邻接矩阵不行,那我们就用时间复杂度更优的邻接表,将 \(now\) 的所有出边排序即可。链式前向星对于排序出边的操作有些困难,而 vector 则容易得多,所以选用 vector

sort 代码:

for(int i = 1 ; i <= n ; i ++)sort(G[i].begin(), G[i].end());

dfs 代码:

void dfs(int now){
  for(int i = a[now] ; i < g[now].size() ; i = a[now])
  	a[now] = i + 1,dfs(g[now][i]);
  s.push(now);
}
//其中 a[now] 表示 G[now][1,2……,a[now] - 1] 都已经被标记访问过,下一次要从 G[now][a[now]] 开始访问。

dfs 时间复杂度:\(O(n)\)。

sort 时间复杂度:\(O(m\log m)\)。

总时间复杂度:\(O(n+m\log m)\)。

足以 AC 本题。

#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
#define MAXN 100005
using namespace std;
int n, m, u, v;
int cnt1, cnt2, tmp = 1;
int a[MAXN], rd[MAXN], cd[MAXN];
bool flag = true;
stack <int> s;
vector <int> g[MAXN];
int read(){
    int t = 1, x = 0;char ch = getchar();
    while(!isdigit(ch)){if(ch == '-')t = -1;ch = getchar();}
    while(isdigit(ch)){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * t;
}
void write(int x){
    if(x < 0){putchar('-');x = -x;}
    if(x >= 10)write(x / 10);
    putchar(x % 10 ^ 48);
}
void dfs(int now){
	for(int i = a[now] ; i < g[now].size() ; i = a[now])
		a[now] = i + 1,dfs(g[now][i]);
	s.push(now);
}
int main(){
	n = read();m = read();
    for(int i = 1 ; i <= m ; i ++){
        u = read();v = read();
        g[u].push_back(v);
        cd[u]++;rd[v]++;
    }
    for(int i = 1 ; i <= n ; i ++)sort(g[i].begin(), g[i].end());
    for(int i = 1 ; i <= n ; i ++){
        if(cd[i] != rd[i])flag = false;
        if(rd[i] - cd[i] == 1)cnt1++;
        if(cd[i] - rd[i] == 1)
            cnt2++,tmp = i;
    }
    if(flag == false)
        if(cnt1 != cnt2 || cnt1 != 1){
            puts("No");return 0;
    }
    dfs(tmp);
    while(!s.empty())
        write(s.top()),putchar(' '),s.pop();
    return 0;
}

标签:int,路径,dfs,学习,笔记,顶点,now,欧拉
From: https://www.cnblogs.com/tsqtsqtsq/p/17794199.html

相关文章

  • C#基础代码学习
    usingSystem;usingSystem.Collections.Generic;publicclassStudent{publicstringName{get;set;}}classMyClass{//用于存储学生对象的集合privateList<Student>test;//构造方法示例话调用类似PHP中的__constructpublicMyClass()......
  • 学习笔记:二分图
    二分图引入二分图又被称为二部图。二分图就是可以二分答案的图。二分图是节点由两个集合组成,且两个集合内部没有边的图。换言之,存在一种方案,将节点划分成满足以上性质的两个集合。性质如果两个集合中的点分别染成黑色和白色,可以发现二分图中的每一条边都一定是连接一个黑色......
  • 学习笔记:拓扑排序
    拓扑排序引入拓扑排序是一个有向无环图的所有顶点的线性序列。该序列需要满足每个顶点出现且只出现一次和如果有一条AA到BB的路径,在序列中AA出现在BB的前面。实现拓扑排序的步骤:计算每个点的入度。入度为\(0\)就加入队列。当队列不为空则循环:取出队首元素并......
  • javaweb学习每日总结-第八天
    第八天学习Springboot今天也终于是学到了springboot的技术,springboot是一款Java开发的框架,也是当下最流行的开发方式,没有之一!今天我进行了springboot技术的入门,初步了解了springboot技术的发展和应用,也用idea写了一个最简单的springboot程序。除此之外,我还下载了postmen这个软......
  • 学习笔记7
    第三章第四章并发编程并行计算并行计算是一种计算方案,它尝试使用多个执行并行算法的处理器更快速的解决问题顺序算法与并行算法并行性与并发性并行算法只识别可并行执行的任务。CPU系统中,并发性是通过多任务处理来实现的线程线程的原理某进程同一地址空间上的独立执行......
  • yzy第七周学习笔记
    第四章并发编程4.1并行计算导论Linux环境中有很多应用程序和很多进程,其中最重要的是客户端网络/服务器。多进程服务器是指当客户端发出请求时,服务器使用子进程来处理客户端的请求。父进程继续等待来自其他客户端的请求。这种方法的优点是服务器可以在客户端请求时管理客户......
  • php代码审计学习----蜜蜂cms代码审计
    php代码审计学习----蜜蜂cms代码审计源码https://github.com/Betsy0/CMSVulSource/tree/main/beescms环境搭建这个需要用docker搭建环境用windows的phpstudy会出现403然后chmod-R777html在docker容器里mysql-uroot-prootcreatedatabasebeescms;然后再/etc/mysq......
  • 学习笔记七
    学习笔记七一、作业要求自学教材第4章,提交学习笔记(10分),评分标准如下知识点归纳以及自己最有收获的内容,选择至少2个知识点利用chatgpt等工具进行苏格拉底挑战,并提交过程截图,提示过程参考下面内容(4分)“我在学***X知识点,请你以苏格拉底的方式对我进行提问,一次一个问题”核......
  • 阅读笔记5
    领域驱动设计的最佳实践领域驱动设计(DDD)有一些最佳实践,可以帮助您更好地应对软件核心复杂性:建模与沟通:建立共享的领域模型,确保开发团队和领域专家之间的共同理解。使用通用语言来描述领域对象和操作。持续演化:领域模型是一个持续演化的过程。随着对业务需求的深入了解,不断改进和......
  • uboot中am335x的relocate分析--Apple的学习笔记
    一,前言今天我主要先分析下bbblack的relocate。至于为什么要分析这块内容,因为我个人理解,内存分布也是重要内容,最关键的是这些内容我3年前分析过TQ2440的,但是没分析过bbblack的,所以补上。二,实践先在board_f.c中添加#define_DEBUG1就支持debug函数打印信息了。U-Boot2023.10(Oc......