[模板][持续更新]欧拉回路与欧拉路径浅析

Luogu P2731 骑马修栅栏 Riding the Fences

题目背景

Farmer John每年有很多栅栏要修理。他总是骑着马穿过每一个栅栏并修复它破损的地方。

题目描述

John是一个与其他农民一样懒的人。他讨厌骑马,因此从来不两次经过一个栅栏。你必须编一个程序,读入栅栏网络的描述,并计算出一条修栅栏的路径,使每个栅栏都恰好被经过一次。John能从任何一个顶点(即两个栅栏的交点)开始骑马,在任意一个顶点结束。

每一个栅栏连接两个顶点,顶点用1到500标号(虽然有的农场并没有500个顶点)。一个顶点上可连接任意多(>=1)个栅栏。两顶点间可能有多个栅栏。所有栅栏都是连通的(也就是你可以从任意一个栅栏到达另外的所有栅栏)。

你的程序必须输出骑马的路径(用路上依次经过的顶点号码表示)。我们如果把输出的路径看成是一个500进制的数,那么当存在多组解的情况下,输出500进制表示法中最小的一个 (也就是输出第一位较小的,如果还有多组解,输出第二位较小的,等等)。

输入数据保证至少有一个解。

输入输出格式

输入格式:

第1行: 一个整数F(1 <= F <= 1024),表示栅栏的数目

第2到F+1行: 每行两个整数i, j(1 <= i,j <= 500)表示这条栅栏连接i与j号顶点。

输出格式:

输出应当有F+1行,每行一个整数,依次表示路径经过的顶点号。注意数据可能有多组解,但是只有上面题目要求的那一组解是认为正确的。

输入输出样例

输入样例#1:

9
1 2
2 3
3 4
4 2
4 5
2 5
5 6
5 7
4 6

输出样例#1:

1
2
3
4
2
5
4
6
5
7

说明

题目翻译来自NOCOW。

USACO Training Section 3.3

Solution:

来系统地整理一下有关欧拉回路和欧拉路径的问题。

什么是欧拉路径?在图上用一种走法经过所有的边一次且只有一次的路径叫做欧拉路径。即一笔画。

如果这条路径的起点和终点重合,那么就是欧拉回路。

如何判断图是否有欧拉回路或者欧拉路径?

无向图:因为欧拉路径中,除了起点与终点以外,任意点的“进”“出”次数相等,所以除了两个点为奇点(度数为奇数的点)(终点和起点)以外,其它点的度数均为偶数。

如果是欧拉回路,奇点的个数应该为0。

有向图:欧拉路径中,最多只有两个点的入度不等于出度。起点出度比入度大1,终点入度比出度大1。

如果是欧拉回路,所有点的 入度=出度 。

寻找欧拉回路或欧拉路径的算法有?

Fluery算法和Hierholzers算法。后者好像也有博客称逐步插入回路法。

后面一种算法无论是编程复杂度还是时间复杂度好像都比前种算法复杂度更优,但前者的应用广泛性好像比后者更高。欧拉回路的更高级的应用还没涉及,如果之后有什么新内容再来补充吧....所以这里就用Hierholzers算法了。

Hierholzers算法自动寻找欧拉回路,在找不到欧拉回路的情况下会找到欧拉路径。前提是得给它指定好起点。

算法流程(无向图):

1.判断奇点数。奇点数若为0则任意指定起点,奇点数若为2则指定起点为奇点。

2.开始递归函数Hierholzers(x):
  循环寻找与x相连的边(x,u):
    删除(x,u)
    删除(u,x)
    Hierholzers(u);
  将x插入答案队列之中

3.倒序输出答案队列

对于该图,算法的执行流程如下:
1.找到该图没有奇点,从1开始进行Hierholzers算法。
2.删边1-2 递归到2
3.删边2-3 递归到3
4.删边3-7 递归到7
5.删边7-1 递归到1
6.1无边,1加入队列,返回
7.7加入队列,返回
8.删边3-4 递归到4
9.删边4-5 递归到5
10.删边5-6 递归到6
11.删边6-3 递归到3
12.3加入队列,返回
13.6加入队列,返回
14.5加入队列,返回
15.4加入队列,返回
16.3加入队列,返回
17.2加入队列,返回
18.1加入队列,返回

答案队列为:1 7 3 6 5 4 3 2 1。反向输出即为答案。

有向图除判断是否存在有一点点不同以外同理。

对于该题【欧拉路径/欧拉回路】模板题,要求输出答案的最小序列。所以起点首先要选的尽量小,然后在边的储存上面加一点小trick。
使用邻接表储存图时,除了用链式前向星还可以用vector储存。我们可以把vector换成multiset,这样就可以保证该点前往的下一个点是最小值,同时保证了答案的最小值。

下面是该题的代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1025;
multiset<int> to[N];
int len[N];
int road[N],k;
void dfs(int x){
    for(auto a=to[x].begin();a!=to[x].end();a=to[x].begin()){//auto类型为C++11标准,可进行自动类型推断
        int u=*a;
        to[x].erase(a);
        to[u].erase(to[u].find(x));//删边
        dfs(u);//递归
    }
    road[k++]=x;//往答案队列里插入答案
}

int main(){
    int m,a,b;
    scanf("%d",&m);
    for(int i=0;i<m;i++){
        scanf("%d%d",&a,&b);
        len[a]++,len[b]++;
        to[a].insert(b);
        to[b].insert(a);
    }
    int s=-1,e=-1;//起点与终点
    for(int i=1;i<=1024;i++)
        if(len[i]%2==1){
            if(s==-1)s=i;
            else if(e==-1)e=i;
            else exit(1);
        }//判断每个点的度数
    if(s==-1)s=1;
    dfs(s);//开始递归
    for(k=k-1;k>=0;k--)
        printf("%d\n",road[k]);//倒序输出答案
    return 0;
}

之后会填Fluery算法和欧拉路径更高级应用的坑。(大概)

时间: 2024-11-08 06:26:43

[模板][持续更新]欧拉回路与欧拉路径浅析的相关文章

比赛模板(持续更新中)

1.数据结构 (1)线段树单点更新 #include <cstdio> #include <cstdlib> #include <algorithm> #include <cstring> #include <cmath> #define MAXN (1<<19) using namespace std; int segTree[MAXN]; void update(int i, int lft, int rht, int index

东拼西凑的模板&#183;持续更新中

一.常用算法(应该不需要用到模板的那种常用) 1.1 快速幂 1 //https://blog.csdn.net/java_c_android/article/details/55802041 2 long long quickmod(long long a,long long b,long long m) 3 { 4 long long ans = 1; 5 while(b)//用一个循环从右到左便利b的所有二进制位 6 { 7 if(b&1)//判断此时b[i]的二进制位是否为1 8 { 9

树状数组模板(持续更新)

树状数组题目(持续更新) \(1.\) 树状数组 \(1\) :单点修改,区间查询 \(2.\) 树状数组 \(2\) :区间修改,单点查询 \(3.\) 树状数组 \(3\) :区间修改,区间查询 树状数组单点修改,区间查询和 $View$ $Code$ //省略头文件 using namespace std; inline int read() { int ret=0,f=1; char ch=getchar(); while(ch>'9'||ch='0'&&ch 树状数组区间修

前端面试题总结(js、html、小程序、React、ES6、Vue、算法、全栈热门视频资源)持续更新 &#362414;

原文: http://blog.gqylpy.com/gqy/438 置顶:来自一名75后老程序员的武林秘籍--必读(博主推荐) 来,先呈上武林秘籍链接:http://blog.gqylpy.com/gqy/401/ 你好,我是一名极客!一个 75 后的老工程师! 我将花两分钟,表述清楚我让你读这段文字的目的! 如果你看过武侠小说,你可以把这个经历理解为,你失足落入一个山洞遇到了一位垂暮的老者!而这位老者打算传你一套武功秘籍! 没错,我就是这个老者! 干研发 20 多年了!我也年轻过,奋斗过!我

动态加载页面数据的小工具 javascript + jQuery (持续更新)

使用该控件,可以根据url,参数,加载html记录模板(包含json参数对应,以及具体记录位置Index根据参数描述加载对应的属性,并可以根据简单的判断分支加载对应html或者控件)至列表容器内(JQuery选择器字符串)注: 该控件在使用前需引入JQuery框架支持,使用该控件,可极大的减少Ajax列表数据动态加载开发工作的实际工作量. 使用方式: 首先,添加控件引用,并加入Jquery支持 <script src="js/jquery.js"></script&g

android开发开源宝贝——持续更新。。。

2016年11月11日更新 http://www.apkbus.com/forum-417-1.html http://p.codekk.com/detail/Android/hejunlin2013/LivePlayback www.codekk.com https://github.com/Trinea/android-open-project Android 开源项目分类汇总 我们的微信公众号:codekk.二维码如下: 专注于 Android 开源分享.源码解析.框架设计.Android

干货!IT小伙伴们实用的网站及工具大集合!持续更新!

干货!IT小伙伴们实用的网站及工具大集合!持续更新! Other  崔庆才  4个月前 (12-24)  6720℃  7评论 1.Git 还在担心自己辛辛苦苦写的代码被误删了吗?还在担心自己改错了代码不能挽回吗?还在苦恼于多人开发合作找不到一个好的工具吗?那么用Git就对了,Git是一个开源的分布式版本控制系统,用以有效.高速的处理从很小到非常大的项目版本管理.有了它,代码托管不是问题,版本控制不再苦恼,多人开发变得简单易行. 链接:http://git-scm.com/ 2.GitHub 学

nodejs学习(持续更新中)

nodejs和express的安装什么的,网上基本都有现成的了,这里有点说下, 在较早点的版本(如3.5.0) npm install -g [email protected] 后,可以直接使用 express helloWorld创建工程, 但最新express4.0版本中将命令工具分家出来了(项目地址:https://github.com/expressjs/generator),所以我们还需要安装一个命令工具,命令如下:npm install -g express-generator ##

【持续更新】JavaScript常见面试题整理

[重点提前说]这篇博客里的问题涉及到了JS中常见的的基础知识点,也是面试中常见的一些问题,建议初入职场的园友Mark收藏,本文会持续更新~ 1. 引入JS的三种方式 1.在HTML标签中直接使用,直接内嵌JS(但是不提倡使用): >>>不符合W3C关于内容和行为分离的要求: 2.在HTML页面中使用<scrip> </script>标签包裹JS代码: >>>script标签可以放到页面的各种位置: 3.引入外部的JS文件使用<script&