欧拉回路与欧拉路(模板)

欧拉回路

欧拉图: 就是从任意一个点开始都可以一笔画完整个图
半欧拉图: 必须从某个点开始才能一笔画完整个图。

对于无向图 , 是欧拉图当且仅当 是连通的且没有奇度顶点。

对于无向图 , 是半欧拉图当且仅当 是连通的且 中恰有 个或 个奇度顶点。

对于有向图 , 是欧拉图当且仅当 的所有顶点属于同一个强连通分量且每个顶点的入度和出度相同。

对于有向图 , 是半欧拉图当且仅当
如果将有向图中的所有有向边退化为无向边时,那么所有顶点属于同一个连通分量。
最多只有一个顶点的出度与入度差为 。
最多只有一个顶点的入度与出度差为 。
所有其他顶点的入度和出度相同。

Fleury

一个偏暴力的算法,应用范围更广,但是复杂度高,暂时用不到,以后再学。

在这里插入图片描述

Hierholzer(逐步插入回路法)

先记录每个点的度,判断是否存在欧拉回路或者路
从一个指定起点出发,不断的走与其相邻的边,走过的边就删掉,回溯时碰到出入度为0的点就将其加入答案队列,显然答案是逆序加入的,所以要从队尾开始输出。

该算法,有回路给出回路,没回路给出路,都没有则结束
参考他的手推递归树方便理解

以一道例题给出模板
P2731 [USACO3.3]骑马修栅栏 Riding the Fences

/*
Hierholzer
*/ 
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1025;
multiset<int> to[maxn]; // 要使输出的路径尽可能小,利用multiset的排序
int degree[maxn];
int road[maxn], cnt; // 记录路径及路径长度 
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[cnt++] = x; // 往队列里插答案 
} 

int main() {
	int m, a, b;
	scanf("%d", &m);
	for(int i = 0; i < m; i++) {
		scanf("%d%d", &a, &b);
		degree[a]++, degree[b]++; // 记录点的度 
		to[a].insert(b); // 建边 
		to[b].insert(a); // 双向边 
	}
	int s = -1, e = -1; // 起点与终点 
	for(int i = 1; i <= 1024; i++) {// 判断每个点的度数 
		if(degree[i]&1) { // 奇度顶点 
			if(s == -1) s = i; // 一个作为起点 
			else if(e == -1) e = i; // 一个作为终点 
			else exit(1); // 如果奇度顶点 >= 3 没有欧拉路 
			// 退出整个程序,终止进程,并返回1给操作系统。
			// 由于返回0代表程序正常退出,返回1等其他数字通常代表异常终止。可通过返回的具体数值判断出错源。
		} 
	}
	if(s == -1) s = 1; // 全是偶度顶点则直接从 开始答案最小 
	dfs(s);
	for(int i = cnt-1; i >= 0; i--) { // 倒叙输出为答案 
		printf("%d\n", road[i]);
	} 
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值