第三章 图论 No.12欧拉回路与欧拉路径

文章目录

    • 定义
      • 欧拉路径的性质:1123. 铲雪车
      • 边编号输出欧拉路径:1184. 欧拉回路
      • 点编号字典序最小输出欧拉路径:1124. 骑马修栅栏
      • 并查集判断有向图是否存在欧拉路径:1185. 单词游戏

定义

小学一笔画问题,每条边只经过一次

判断图是否存在欧拉回路:判断图是否连通(存在孤立边),再根据有向/无向具体判断

对于无向图来说,欧拉路径中,起点和终点的度数为奇数,中间点的度数为偶数
起点和终点:开始和结束时必须经过一条边,其余情况为:从一条边进入,再从另一条边离开,即度数为1 + 2 * n
中间点:一条边进入,一条边离开,度数为2 * n

欧拉回路中,所有点的度数为偶数
image.png

七桥问题中,由于每个点的度数为奇数,所以不可能存在欧拉路径
以下结论直接记:
image.png


欧拉路径的性质:1123. 铲雪车

1123. 铲雪车 - AcWing题库
image.png

根据题意,所有的街道都是双向,说明这张图是一张有向图。每个城市都连接了街道,说明这张图是连通图。将每个城市看成点,那么每个点的入度都等于出度,这张图存在欧拉回路,所以无论如何,我们都有一条路径,能够不重复不遗漏的遍历所有的边,而这道题要求时间(最短路径),我们不需要算出具体的欧拉回路,以为欧拉回路的距离一定是所有路径之和,只要根据题意将路径转换成时间即可

#include <iostream>
#include <cmath>
using namespace std;

int main()
{
    double x1, y1, x2, y2;
    scanf("%lf%lf", &x1, &y1);
    double sum = 0;
    while (~scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2))
    {
        double x = x1 - x2, y = y1 - y2;
        sum += sqrt(x * x + y * y) * 2;
    }
    
    int mte = round(sum / 1000 * 60 / 20);
    int hour = mte / 60;
    mte %= 60;
    printf("%d:%02d\n", hour, mte);
    return 0;
}

debug:距离要乘2,每条道路都是双向的


边编号输出欧拉路径:1184. 欧拉回路

1184. 欧拉回路 - AcWing题库
image.png

欧拉路径的板子:
若一张图存在欧拉路径,可以用dfs递归完所有的边,在dfs回溯时记录边的编号到数组中,最后逆着输出该数组即可
用uesd数组标记已经遍历过的边,同时每遍历完一条边就删除这条边,防止重复遍历
其实有向图不需要使用used数组,只要删除遍历完的边即可
开uesd数组是因为无向图,由于我们用单链表存储边,虽然可以删除当前正在遍历的边,但是在无向图中,删除反向边比较麻烦,所以这里用uesd数组标记反向边,保证每条边只会遍历一次

需要注意,不要遍历孤立点,若图中存在欧拉路径,孤立点不会影响欧拉路径
还要注意,这题的无向图的欧拉回路中,若边的方向与输入给定的方向不同,那么需要输出编号的负数

#include <iostream>
#include <cstring>
using namespace std;

const int N = 1e5 + 10, M = 4e5 + 10;
int h[N], e[M], ne[M], idx;
int din[N], dout[N];
int ans[M / 2], cnt;
bool used[M];
int type, n, m;

void add(int x, int y)
{
    e[idx] = y, ne[idx] = h[x], h[x] = idx ++ ;
}

void dfs(int x)
{
    for (int &i = h[x]; i != -1;)
    {
        if (used[i])
        {
            i = ne[i];
            continue;
        }
        
        int t; // 当前边的编号
        used[i] = true;
        if (type == 1) 
        {
            used[i ^ 1] = true;
            t = i / 2 + 1;
            if (i & 1) t = -t;
        }
        else t = i + 1;
        
        int y = e[i];
        i = ne[i];
        dfs(y);
        
        ans[++ cnt] = t;
    }
}

int main()
{
    memset(h, -1, sizeof(h));
    cin >> type >> n >> m;
    int x, y;
    for (int i = 0; i < m; ++ i )
    {
        scanf("%d%d", &x, &y);
        add(x, y);
        if (type == 1) add(y, x);
        din[y] ++ , dout[x] ++ ;
    }
    
    if (type == 1)
        for (int i = 1; i <= n; ++ i )
            if ((din[i] + dout[i]) % 2)
            {
                puts("NO");
                return 0;
            }

    if (type == 2)
        for (int i = 1; i <= n; ++ i )
            if (din[i] != dout[i])
            {
                puts("NO");
                return 0;
            }
            
    for (int i = 1; i <= n; ++ i )
        if (h[i] != -1)
        {
            dfs(i);
            break;
        }
    
    if (cnt < m)
    {
        puts("NO");
        return 0;
    }
    puts("YES");
    for (int i = m; i ; -- i )
        printf("%d ", ans[i]);
    puts("");
    
    return 0;
}

要特别注意:求欧拉路径后,必须要判断这条路径是否遍历了所有的边


点编号字典序最小输出欧拉路径:1124. 骑马修栅栏

1124. 骑马修栅栏 - AcWing题库
image.png

题目要求按照点的编号最小字典序输出欧拉路径,可以dfs(x)时,可以先遍历与x相连的编号较小的点,那么与x相连的点中,相较于编号较大的点,编号较小的点将被存储到序列的靠后位置,逆序后编号较小的点位于序列的靠前位置,满足最小字典序

为什么不使用邻接表存储图?
用邻接表存储图时,选择编号较小的点会比较麻烦,需要对单链表中的元素进行排序
用邻接矩阵存储图,直接按照下标从小到大dfs与x相连的点即可

需要注意的是,题目可能存在重边,通常邻接矩阵存储边权的最小/大值,但是这题需要存储两点间边的数量,以保证之后的dfs中每条边都被遍历
还有一点:图是无向图,但题目没有说存在欧拉回路还是欧拉路径,如果存在欧拉路径,那么起点和终点的度数为奇数。如果存在欧拉回路,那么所有点的度数为偶数
所以dfs之前需要找度数为奇数的点,若找到,说明该图一定存在欧拉路径,可能不存在欧拉回路。此时从度数为偶数的点dfs将无法正确遍历欧拉路径,只有存在欧拉回路的情况下,才能从度数为偶数的点dfs
并且,1不是最小的点编号,点编号范围在1~500,所以要找到一个度数非0的点编号,再找是否存在欧拉路径,最后再dfs
由于使用邻接矩阵存储无向图,所以不需要开used数组,每次遍历一条边,能够很简单地删除这条边和其反向边

#include <iostream>
using namespace std;

const int N = 510, M = 2100;
int g[N][N], d[N];
int ans[M], cnt;
int n, m;

void dfs(int x)
{
    for (int y = 1; y < N; ++ y )
    {
        if (g[x][y])
        {
            g[x][y] -- , g[y][x] --;
            dfs(y);
        }
    }
    ans[ ++ cnt ] = x;
}

int main()
{
    scanf("%d", &m);
    int x, y;
    for (int i = 0; i < m; ++ i )
    {
        scanf("%d%d", &x, &y);
        g[x][y] ++ , g[y][x] ++ ;
        d[x] ++ , d[y] ++ ;
    }
    
    int start = 1;
    while (!d[start]) start ++ ;
    for (int i = start; i < N; ++ i )
        if (d[i] % 2)
        {
            start = i;
            break;
        }

    dfs(start);

    for (int i = cnt; i; -- i ) printf("%d\n", ans[i]);
    return 0;
}

并查集判断有向图是否存在欧拉路径:1185. 单词游戏

1185. 单词游戏 - AcWing题库
image.png

建图方式:以单词的第一个字母或最后一个字母为点,从前往后建一条有向边
问题转换成:判断有向图中是否存在欧拉路径,当然可能也是欧拉回路
由于不需要找出具体的欧拉路径,所以可以不用dfs,只要满足所有点的入度等于出度(欧拉回路),或者(欧拉路径)入度比出度多1(终点),出度比入度多1(起点)
该图就存在欧拉路径,当然,还需要判断是否有孤立边,由于这题特殊的建图方式,若存在一个单词是孤立的,那么在图中表现为两点一边的孤立
判断是否存在孤立边,可以dfs跑一遍欧拉路径,判断边数是否小于图的边数
但是可以用更简单的并查集,维护每个点属于的集合,建完图后,一旦出现两点属于不同集合,那么图中一定存在孤立边(因为该图中的点一定连接着一条边,所以点孤立=边孤立)

#include <iostream>
#include <cstring>
using namespace std;

const int N = 30, M = 1e5 + 10;
bool st[N]; 
int din[N], dout[N];
int p[N];
int T, m;

int find(int x)
{
    if (x != p[x]) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    scanf("%d", &T);
    while (T -- )
    {
        memset(st, false, sizeof(st));
        memset(din, 0, sizeof(din));
        memset(dout, 0, sizeof(dout));
        for (int i = 0; i < 26; ++ i ) p[i] = i;
        char str[1010];
        scanf("%d", &m);
        for (int i = 0; i < m; ++ i )
        {
            scanf("%s", str);
            int len = strlen(str);
            int x = str[0] - 'a', y = str[len - 1] - 'a';
            st[x] = st[y] = true;
            din[y] ++ , dout[x] ++ ;
            p[find(x)] = p[find(y)];
        }
        
        int start = 0, end = 0; // 起点和终点的数量
        bool flag = true;
        for (int i = 0; i < 26; ++ i )
            if (din[i] != dout[i])
            {
                if (din[i] + 1 == dout[i]) end ++ ;
                else if (din[i] == dout[i] + 1) start ++ ;
                else 
                {
                    flag = false;
                    break;
                }
            }
            
        if (flag && !((!start && !end) || (start == 1 && end == 1))) // 起点数和终点数不正确
            flag = false;
        // 判断是否存在孤立边
        int t = -1;
        for (int i = 0; i < 26; ++ i )
            if (st[i])
            {
                if (t == -1) t = find(i);
                else if (t != find(i))
                {
                    flag = false;
                    break;
                }
            }
            
        if (flag) puts("Ordering is possible.");
        else puts("The door cannot be opened.");
    }
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/75329.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Neo4j的使用场景_以及Windows版安装_欺诈检测_推荐_知识图谱---Neo4j图数据库工作笔记0003

可以看到使用场景,比如欺诈检测, 要建立图谱,才能进行,欺诈人员检测 可以看到图谱的各种应用场景 然后推荐引擎也需要,可以看到 在金融,旅行,求职招聘,保健,服务,媒体娱乐,都可以进行推荐 然后还有知识图谱 身份访问管理,这里,可以进行安全管理,可以挖掘出潜在关系,分析, 某…

Chapter 2 Crystal Dynamics 2.1 晶格振动

2.1 Lattice Vibration 晶格振动 Born-Oppenheimer Approximation Electrons’ movement: Electron theory free electron theoryenergy band theory Atoms’ movement crystal dynamicslattice vibration 当研究电子运动时&#xff0c;忽略原子运动&#xff1b;当研究原子…

JavaScript如何执行语句

目录 语法/词法分析 预编译 解释执行 预编译什么时候发生 js运行三步曲 预编译前奏 预编译步骤 巩固基础练习 语法/词法分析 按语句块的粒度解析成抽象语法树 ,分析该js脚本代码块的语法是否正确&#xff0c;如果出现不正确&#xff0c;则向外抛出一个语法错误&#x…

NSI45030AT1G LED驱动器方案为汽车外部及内部照明恒流稳流器(CCR)方案

关于线性恒流调节器&#xff08;CCR&#xff09;&#xff1a;是一种用于控制电流的稳定输出。它通常由一个功率晶体管和一个参考电流源组成。CCR的工作原理是通过不断调节功率晶体管的导通时间来维持输出电流的恒定。当输出电流超过设定值时&#xff0c;CCR会减少功率晶体管的导…

探索未来:元宇宙与Web3的无限可能

随着科技的奇迹般发展&#xff0c;互联网已经成为了我们生活的不可分割的一部分。然而&#xff0c;尽管它的便利性和普及性带来了巨大的影响&#xff0c;但我们仍然面临着传统互联网体验的诸多限制。 购物需要不断在实体店与电商平台间切换&#xff0c;教育依然受制于时间与地…

数据库信息速递 -- MariaDB 裁员后,前景不确定 (翻译)

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请加 liuaustin3微信号 &#xff0c;在新加的朋友会分到3群&#xff…

【教程】零成本将小米净化器改造为无叶风扇

某宝某多上&#xff0c;就这么点破塑料&#xff0c;就要买79&#xff1f;&#xff01;&#xff01; 我这枚韭菜可不上当。咱自己做一个&#xff01; 真香~

UNIX网络编程——TCP协议API 基础demo服务器代码

目录 一.TCP客户端API 1.创建套接字 2.connect连接服务器​编辑 3.send发送信息 4.recv接受信息 5.close 二.TCP服务器API 1.socket创建tcp套接字(监听套接字) 2.bind给服务器套接字绑定port,ip地址信息 3.listen监听并创建连接队列 4.accept提取客户端的连接 5.send,r…

Linux设备树详解

Linux 设备树详解 Linux 操作系统早期是针对个人电脑设备而开发的操作系统&#xff0c;而个人电脑处理器产商较为单一&#xff08;例如只有 Intel&#xff0c;AMD&#xff09;同时个人电脑产商均使用 Intel 或 AMD 制造的处理器&#xff0c;业界形成了统一的总线/硬件接口标准…

SQLSERVER 查询语句加with (NOLOCK) 报ORDER BY 报错 除非另外还指定了 TOP、OFFSET 或 FOR XML

最近有一个项目在客户使用时发现死锁问题&#xff0c;用的数据库是SQLSERVER &#xff0c;死锁的原因是有的客户经常去点报表&#xff0c;报表查询时间又慢&#xff0c;然后又有人在做单导致了死锁&#xff0c;然后主管要我们用SQLSERVER查询时要加with (NOLOCK),但是我在加完 …

Excel设置某列或者某行不某行不可以编辑,只读属性

设置单元格只读的三种方式: 1、通过单元格只读按钮&#xff0c;设置为只为 设置行或者列的只读属性&#xff0c;可以设置整行或者整列只读 2、设置单元格编辑控件为标签控件(标签控件不可编辑) 3、通过锁定行&#xff0c;锁定行的修改。锁定的行与只读行的区别在于锁定的行不…

【已解决】mac端 sourceTree 解决remote: HTTP Basic: Access denied报错

又是在一次使用sourcetree拉取或者提交代码时候&#xff0c;遇到了sourcetree报错&#xff1b; 排查了一会&#xff0c;比如查看了SSH keys是否有问题、是否与sourcetree账户状态有问题等等&#xff0c;最终才发现并解决问题 原因&#xff1a; 因为之前公司要求企业gitlab中…

智安网络|网络安全:危机下的创新与合作

随着信息技术的迅猛发展和互联网的普及&#xff0c;我们进入了一个高度网络化的社会。网络在提供便利和连接的同时&#xff0c;也带来了许多安全隐患和挑战。 一、网络安全的危险 **1.数据泄露和隐私侵犯&#xff1a;**网络上的个人和机构数据存在遭受泄露和盗取的风险&#…

什么是Node js?什么是React?有什么区别

JavaScript是当今最流行的编程语言之一&#xff0c;它用于开发多种技术&#xff0c;两种这样的技术是Node.js和React。许多学生很难理解Nodejs和React之间的区别。 React和Nodejs之间的主要区别在于它们的使用位置。Nodejs 用于开发应用程序的服务器端&#xff0c;而Reactjs用于…

docker 学习--03 环境安装(本人使用的win10 Linux也是在win10下模拟)

docker 学习–03 环境安装&#xff08;本人使用的win10 Linux也是在win10下模拟&#xff09; 文章目录 docker 学习--03 环境安装&#xff08;本人使用的win10 Linux也是在win10下模拟&#xff09;[TOC](文章目录) 1. windows10 安装docker1.1 访问官网 点击下载1.2.点击下载的…

HTTP和HTTPS协议

目录 一、HTTP和HTTPS区别&#x1f33b; 二、有了https还有使用http场景吗&#x1f34a; 三、https协议的工作原理&#x1f4a5; 四、https协议的优点和缺点&#x1f35e; 一、HTTP和HTTPS区别&#x1f33b; HTTP&#xff08;Hypertext Transfer Protocol&#xff09;和HTT…

Freemarker+thymeleaf应用实现打印银行小票

背景&#xff1a;最近项目里有个需求&#xff0c;需要动态配置一个模板&#xff0c;来打印各种不同银行或者其他行业的小票&#xff0c;下面小小记录一下实现过程。 关键词&#xff1a;Springboot, thymeleaf, Freemarker,html2image 一&#xff0c;引入依赖 <dependency…

【算法——双指针】LeetCode 202 快乐数

题目描述&#xff1a; 思路&#xff1a;快慢指针 看到循环&#xff0c;我就想起了快慢指针的方法&#xff0c;从题目我们可以看出&#xff0c;我们需要模拟一个过程&#xff1a;不断用当前的数去生成下一个数&#xff0c;生成的规则就是将当前数的各位的平方累加&#xff1b; …

项目:基于UDP的TFTP文件传输

1&#xff09;tftp协议概述 简单文件传输协议&#xff0c;适用于在网络上进行文件传输的一套标准协议&#xff0c;使用UDP传输特点&#xff1a; 是应用层协议 基于UDP协议实现 数据传输模式 octet&#xff1a;二进制模式&#xff08;常用&#xff09; mail&#xff1a;已经不再…

C++RAII内存管理技术

文章目录 一.什么是RAII内存管理技术&#xff1f;二.智能指针unique_ptrshared_ptr循环引用问题weak_ptr 一.什么是RAII内存管理技术&#xff1f; C在引入异常机制后,代码执行流的跳转变得难以预料,如果使用普通的指针进行内存管理,很难避免内存泄漏的问题(执行流跳转导致堆区…