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

news2024/11/27 12:40:19

文章目录

    • 定义
      • 欧拉路径的性质: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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pconsc4 安装

Pconsc4 安装遇到的问题 Pconsc4-github 按照红框给的一行命令&#xff0c;一行毁所有。 1 gcc and g not found # 1 Start by updating the packages list:sudo apt update# 2 Install the build-essential package by typing:sudo apt install build-essential## The comm…

83. 删除排序链表中的重复元素

题目链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 解题思路&#xff1a;从前往后遍历节点&#xff0c;如果当前节点和下一个节点的值相等&#xff0c;就跳过下一个节点 具体算法如下&#xff1a; current head如果 current!null…

如何使用CSS实现一个下拉菜单?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 使用CSS实现下拉菜单⭐ HTML 结构⭐ CSS 样式⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些…

SAP MIGO采购订单收货默认库存类型控制

在启用了QM模块的企业应用中&#xff0c;收货时针对库存类型就会有不同情况&#xff0c;参与质检的收到质检库存&#xff0c;不参与质检的收到非限制库存。 那么我一直没的遇到过关于应收到质检&#xff0c;而收到非限制库存的反馈。说明这样的问题不会发生&#xff0c;带着疑…

PyQt5设置按钮菜单和信号与槽函数的连接

目录 一、Qt Designer 在窗口中添加菜单和工具栏 二、源代码 1、界面代码 2、逻辑代码 3、演示 三、相关功能 一、Qt Designer 在窗口中添加菜单和工具栏 右键可以选择创建菜单栏。创建后双击【在这里输入】&#xff0c;然后输入菜单名&#xff0c;敲下回车确认&#xf…

OpenCV图像处理——图像平滑

目录 图像噪声椒盐噪声高斯噪声图像平滑简介均值滤波高斯滤波中值滤波 图像噪声 椒盐噪声 随机出现的黑白噪声 高斯噪声 噪声的灰度值符合高斯分布 图像平滑简介 均值滤波 cv.blur(src,ksize,anchor,borderType)import numpy as np import cv2 as cv import matplotlib.…

matlab使用教程(15)—图论基础

1.有向图和无向图 1.1什么是图&#xff1f; 图是表示各种关系的节点和边的集合&#xff1a; • 节点 是与对象对应的顶点。 • 边 是对象之间的连接。 • 图的边有时会有权重 &#xff0c;表示节点之间的每个连接的强度&#xff08;或一些其他属性&#xff09;。 这些定…

【Vue-Router】使用 prams 路由传参失效

报错信息&#xff1a; [Vue Router warn]: Discarded invalid param(s) “name”, “price”, “id” when navigating. list.json {"data": [{"name": "面","price":300,"id": 1},{"name": "水",&quo…

【力扣每日一题】617. 合并二叉树 dfs bfs 8.14打卡

文章目录 题目思路代码 题目 617. 合并二叉树 难度&#xff1a; 简单 描述&#xff1a; 给你两棵二叉树&#xff1a; root1 和 root2 。 想象一下&#xff0c;当你将其中一棵覆盖到另一棵之上时&#xff0c;两棵树上的一些节点将会重叠&#xff08;而另一些不会&#xff0…

AIGC热潮涌动 HashData如何降低大模型应用门槛?

当前&#xff0c;以ChatGPT为代表的大语言模型&#xff08;LLM&#xff09;正在掀起新一轮的AI 浪潮&#xff0c;数字产业生态也迎来前所未有的变局。随着大模型应用的兴起&#xff0c;如何低成本地实现大模型的构建和应用&#xff0c;成为企业关注的重点。 数据仓库是企业数据…

网络安全--筛选给定范围内的日志

目录 pass&#xff1a;在观看此篇前先看上篇的awk介绍 一、文件 二、第一方法 1.步骤 ​编辑三、第二方法&#xff1a; awk内容&#xff1a; 结果&#xff1a; 四、第二要求 统计独立ip 操作步骤&#xff1a; 1.先创建文件写入一下测试内容&#xff1a; 2.书写awk代码…

Redis缓存穿透、击穿和雪崩

1、Redis缓存穿透 缓存穿透是指当用户在查询一条数据的时候&#xff0c;而此时数据库和缓存却没有关于这条数据的任何记录&#xff0c;而这条数据在缓存中没找到就会向数据库请求获取数据。用户拿不到数据时&#xff0c;就会一直发请求&#xff0c;查询数据库&#xff0c;这样…

如何在电力行业运用IPD?

电力行业是国民经济众多垄断行业中较早实施改革的行业之一。近几年我国电力行业保持着较快的发展速度&#xff0c;也取得了很大的成绩&#xff0c;发电机容量和发电量居世界首位。2015-2020年&#xff0c;全国发电量不断攀升。 电力是以电能作为动力的能源。电力的发现和应用掀…

MR300C工业无线WiFi图传模块 内窥镜机器人图像传输有线无线的两种方式

MR300C无线WiFi图传模使用方法工业机器人图像高清传输 ⚫ MR300C图传模块基于MIPS处理器实现&#xff0c;电脑/手机连接模块的WIFI热点或网口即可查看视频流 ⚫ 模块的USB 2.0 Host接口&#xff0c;可接入USB uvc摄像头/内窥镜默认输出的视频格式必须是MJPG ⚫ 模块支持接入摄…

Linux 主函数参数介绍

主函数如下&#xff1a; int main( int argc, char* argv[], char* envp[]) 参数分析如下&#xff1a; (1) argc 参数个数 (2) argv 参数内容&#xff0c;是char*类型&#xff0c;说明传给主函数的内容是一个一个的字符串。 (3) envp 环境变量&#xff0c;传给主函数的也…

蓝牙入耳式耳机老是滑出来,耳朵小适合戴什么样的骨传导耳机

最近体验了几款骨传导耳机&#xff0c;分享下我的使用感受。首先说一下为什么要选择骨传导耳机&#xff0c;我之前是使用入耳式耳机&#xff0c;戴久了耳朵会疼&#xff0c;而且晚上睡觉不能戴。于是就考虑骨传导耳机&#xff0c;因为骨传导耳机在传声的过程中不需要经过耳膜&a…

【Elasticsearch】学好Elasticsearch系列-脚本查询

本文已收录至 Github&#xff0c;推荐阅读 &#x1f449; Java 随想录 先看后赞&#xff0c;养成习惯。 点赞收藏&#xff0c;人生辉煌。 文章目录 概念支持的语言Painless特点简单例子 Scripting的CRUDinsert&#xff08;新增&#xff09;update&#xff08;更新&#xff09;d…

智能工厂:适应不断变化的制造世界

制造业已经从过去传统的装配线工艺流程中走了很长一段路。随着技术的进步和工业 4.0 的兴起&#xff0c;制造业正在迅速发展&#xff0c;以满足现代世界不断变化的需求。近年来出现的一个关键概念就是“智能工厂”。在这篇文章中&#xff0c;我们将探讨什么是智能工厂、它是如何…

为什么要学PMP项目管理?

为什么要学习PMP呢&#xff0c;主要有以下五点&#xff1a; 01提升个人能力 PMP是一个系统学习的过程&#xff0c;充分理解各个项目管理的过程以及项目管理的各个过程组、知识领域等&#xff0c;可以从理论上掌握项目经理应具有的理论素质。能够知道如何对执行的项目进行系统…

【Docker】个人镜像文件Dockerfile制作详解

前言 洁洁的个人主页 我就问你有没有发挥&#xff01; 知行合一&#xff0c;志存高远。 Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux或Windows操作系统的机器上,也可以实现虚拟化,容器是…