欧拉图详解

欧拉图详解

通过图(无向图或有向图)中所有边一次且仅一次行遍图中所有顶点的通路称为欧拉通路,通过图中所有边一次且仅一次行遍所有顶点的回路称为欧拉回路。具有欧拉回路的图称为欧拉图(Euler Graph),具有欧拉通路而无欧拉回路的图称为半欧拉图。

1
定义

欧拉通路(Euler tour)——通过图中每条边一次且仅一次,并且过每一顶点的通路。

欧拉回路 (Eulercircuit)——通过图中每条边一次且仅一次,并且过每一顶点的回路。

2
无向图是否具有欧拉通路或回路的判定

G有欧拉通路的充分必要条件为:G 连通,G中只有两个奇度顶点(它们分别是欧拉通路的两个端点)。

G有欧拉回路(G为欧拉图):G连通,G中均为偶度顶点。

3
有向图是否具有欧拉通路或回路的判定

D有欧拉通路:D连通,除两个顶点外,其余顶点的入度均等于出度,这两个特殊的顶点中,一个顶点的入度比出度大1,另一个顶点的入度比出度小1。

D有欧拉回路(D为欧拉图):D连通,D中所有顶点的入度等于出度。

(注意:这里说有向图连通,说的是有向图是弱连通图。即把有向图中的边变成无向边,只要该图连通,那么原有向图即是弱连通图。实际中可用并查集判断是否弱连通)

注意下面打印节点的代码,其实打印欧拉路径的节点轨迹可以先打印每条欧拉路上的边,然后取每条边的头结点即可(最后一条边还需要取尾部结点)

对于一般的单词首尾相连的问题,一般都是转化为有向图的欧拉通路问题(而不是无向图),比如”给你多个单词,问你能不能将所有单词组成这样一个序列:序列的前一个单词的尾字母与后一个单词的头字母相同”,如果你把每个单词看出无向的边,那么最终求出的欧拉通路可能存在两个单词尾部和尾部相连的情况。

无向欧拉图(半欧拉图)逆序打印欧拉回路或通路代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100+5;

//无向图打印欧拉路径或回路
//输入保证是一个n顶点,m条边的具有欧拉回路或欧拉路径的无向图

int n;//图中顶点,顶点编号1到n
int m;//图中边
int G[maxn][maxn];
int vis[maxn][maxn];//vis[i][j]==1表示i与j点直接存在一条边

//打印欧拉路径或欧拉回路(必须本图有欧拉路径或回路才行)
//打印的结果中最后一条边才是u开始的,打印的第一条边不一定是u开始的
//如果是打印欧拉路径,那么输入的u一定要是起点之一,即度数为奇数的点之一
//否则euler打印的的边不会构成欧拉路径,只不过是乱序打印图中所有的边而已
void euler(int u)
{
    for(int v=1;v<=n;v++)if(vis[u][v]||vis[v][u])
    {
        //递归思想,去掉了u与v这条边,
        //余图还是一个具有欧拉道路的图,且v变成一个起点了
        vis[u][v]=vis[v][u]=0;
        euler(v);
        printf("%d %d\n",u,v);
    }
}

//输出欧拉回路或路径上按顺序经过的节点
//u也必须要是起点之一,否则输出的也是乱序点而已
void euler_point(int u)
{
    for(int v=1;v<=n;v++)if(vis[u][v] || vis[v][u])
    {
        vis[u][v]=vis[v][u]=0;
        euler_point(v);
    }
    printf("%d\n",u);
}

int main()
{
    while(scanf("%d%d",&n,&m)==2)
    {
        memset(G,0,sizeof(G));
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            G[u][v]=G[v][u]=1;//无向图
            vis[u][v]=vis[v][u]=1;
        }

        int u;
        scanf("%d",&u);
        euler_point(u);

    }
    return 0;
}

有向欧拉图(半欧拉图)逆序打印欧拉回路或通路代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100+5;

//有向图打印欧拉路径或回路
//输入保证是一个n顶点,m条边的具有欧拉回路或欧拉路径的有向图

int n;//图中顶点,顶点编号1到n
int m;//图中边
int G[maxn][maxn];
int vis[maxn][maxn];//vis[i][j]==1表示i到j点存在一条边

//打印欧拉路径或欧拉回路(必须本图有欧拉路径或回路才行)
//打印的结果中最后一条边才是u开始的,打印的第一条边不一定是u开始的
//如果是打印欧拉路径,那么输入的u一定要是起点之一,即abs(入度-出度)==1
//且如果是出度-入度==1的点,那么该点就应该是欧拉图的起点,但是会逆序输出,所以最后才输出
//否则euler打印的的边不会构成欧拉路径,只不过是乱序打印图中所有的边而已
void euler(int u)
{
    for(int v=1;v<=n;v++)if(vis[u][v])
    {
        //递归思想,去掉了u与v这条边,
        //余图还是一个具有欧拉道路的图,且v变成一个起点了
        vis[u][v]=0;
        euler(v);
        printf("%d %d\n",u,v);
    }
}

//逆序输出欧拉回路或路径上按顺序经过的节点
//u也必须要是起点之一,否则输出的也是乱序点而已
void euler_point(int u)
{
    for(int v=1;v<=n;v++)if(vis[u][v])
    {
        vis[u][v]=0;
        euler_point(v);
    }
    printf("%d\n",u);
}

int main()
{
    while(scanf("%d%d",&n,&m)==2)
    {
        memset(G,0,sizeof(G));
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            G[u][v]=1;//有向图
            vis[u][v]=1;
        }

        int u;
        scanf("%d",&u);
        euler(u);

    }
    return 0;
}

欧拉图应用

POJ 2513 Colored Sticks(欧拉回路判断+字典树Trie+并查集):其实就是判定是否存在欧拉图,首先欧拉图必须连通,所以需要用并查集。然后本图中的每个节点是一个单词,所以需要用单词映射成节点编号(用map<string,int>映射可能会超时),所以需要用字典序映射。解题报告!

POJ 1041 John‘s trip(欧拉回路+输出路径):输出字典序最小的欧拉回路。解题报告!

POJ 2337 Catenyms(有向欧拉图:输出欧拉路径):有向图输出字典序最小的欧拉路径。解题报告!

POJ 1300 Door Man(欧拉回路判定):判断无向图是否存在欧拉通路。解题报告!

POJ 2230 Watchcow(欧拉回路:输出点轨迹):输出有向图的欧拉回路点轨迹。解题报告!

POJ 1386 Play on Words(判定图欧拉通路是否存在):有向图欧拉道路问题。解题报告!

HDU 1878 欧拉回路(简单欧拉回路判定):简单无向图欧拉问题判定。解题报告!

HDU 3018 Ant Trip(欧拉回路:一笔画问题):给你一个无向的欧拉图,你需要几笔才能一笔画完整个图?解题报告!

时间: 2024-10-08 02:02:05

欧拉图详解的相关文章

Spring事务管理(详解+实例)

写这篇博客之前我首先读了<Spring in action>,之后在网上看了一些关于Spring事务管理的文章,感觉都没有讲全,这里就将书上的和网上关于事务的知识总结一下,参考的文章如下: Spring事务机制详解 Spring事务配置的五种方式 Spring中的事务管理实例详解 1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是要么都执行要么都

转载:DenseNet算法详解

原文连接:http://blog.csdn.net/u014380165/article/details/75142664 参考连接:http://blog.csdn.net/u012938704/article/details/53468483 本文这里仅当学习笔记使用,具体细节建议前往原文细度. 论文:Densely Connected Convolutional Networks 论文链接:https://arxiv.org/pdf/1608.06993.pdf 代码的github链接:h

MariaDB(MySQL)创建、删除、选择及数据类型使用详解

一.MariaDB简介(MySQL简介略过) MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可 MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品.在存储引擎方面,使用XtraDB(英语:XtraDB)来代替MySQL的InnoDB. MariaDB由MySQL的创始人Michael Widenius(英语:Michael Widenius)主导开发,他早前曾以10亿美元的价格,将自己创建的公司MySQL A

HttpServletResponse和HttpServletRequest详解

HttpServletResponse,HttpServletRequest详解 1.相关的接口 HttpServletRequest HttpServletRequest接口最常用的方法就是获得请求中的参数,这些参数一般是客户端表单中的数据.同时,HttpServletRequest接口可以获取由客户端传送的名称,也可以获取产生请求并且接收请求的服务器端主机名及IP地址,还可以获取客户端正在使用的通信协议等信息.下表是接口HttpServletRequest的常用方法. 说明:HttpServ

POSIX 线程详解(经典必看)

总共三部分: 第一部分:POSIX 线程详解                                   Daniel Robbins ([email protected]), 总裁/CEO, Gentoo Technologies, Inc.  2000 年 7 月 01 日 第二部分:通用线程:POSIX 线程详解,第 2部分       Daniel Robbins ([email protected]), 总裁/CEO, Gentoo Technologies, Inc.  20

.NET深入解析LINQ框架(五:IQueryable、IQueryProvider接口详解)

阅读目录: 1.环路执行对象模型.碎片化执行模型(假递归式调用) 2.N层对象执行模型(纵横向对比链式扩展方法) 3.LINQ查询表达式和链式查询方法其实都是空壳子 4.详细的对象结构图(对象的执行原理) 5.IQueryable<T>与IQueryProvider一对一的关系能否改成一对多的关系 6.完整的自定义查询 1]. 环路执行对象模型.碎片化执行模型(假递归式调用) 这个主题扯的可能有点远,但是它关系着整个LINQ框架的设计结构,至少在我还没有搞懂LINQ的本意之前,在我脑海里一直频

netstat状态详解

一.生产服务器netstat tcp连接状态................................................................................ 2 1.1生产服务器某个业务LVS负载均衡上连接状态数量............................................... 2 1.2生产服务器某个业务web上连接状态数量...............................................

详解go语言的array和slice 【二】

上一篇  详解go语言的array和slice [一]已经讲解过,array和slice的一些基本用法,使用array和slice时需要注意的地方,特别是slice需要注意的地方比较多.上一篇的最后讲解到创建新的slice时使用第三个索引来限制slice的容量,在操作新slice时,如果新slice的容量大于长度时,添加新元素依然后使源的相应元素改变.这一篇里我会讲解到如何避免这些问题,以及迭代.和做为方法参数方面的知识点. slice的长度和容量设置为同一个值 如果在创建新的slice时我们把

13.Linux键盘按键驱动 (详解)

版权声明:本文为博主原创文章,未经博主允许不得转载. 在上一节分析输入子系统内的intput_handler软件处理部分后,接下来我们开始写input_dev驱动 本节目标: 实现键盘驱动,让开发板的4个按键代表键盘中的L.S.空格键.回车键 1.先来介绍以下几个结构体使用和函数,下面代码中会用到 1)input_dev驱动设备结构体中常用成员如下: struct input_dev { void *private; const char *name; //设备名字 const char *ph