欧拉回路-积木问题求解

欧拉回路

定义

无向连通图G=(V,E)中的一条经过所有的边的简单回路(道路)称为G的欧拉回路(道路)。

具有欧拉回路的图称为欧拉图(简称E图)。具有欧拉通路但不具有欧拉回路的图称为半欧拉图。

定理

欧拉回路的判断较为简单,具体判断方法如下:

无向连通图G存在欧拉回路的充要条件:

G为连通图且G中各结点的度都是偶数,特别地,当G仅有两个奇度结点(度数为奇数的结点)时,G存在欧拉道路。

一些推论:

  • 当G是仅有两个奇度结点的连通图时,G的欧拉通路必以此两个结点为端点
  • 当G是无奇度结点的连通图时,G必有欧拉回路。

有向图D中存在欧拉回路的充要条件:

D为有向图,D的基图连通,并且所有顶点的出度与入度都相等,特别地若除两个顶点外,其余顶点的出度与入度都相等,而这两个顶点中一个顶点的出度与入度之差为1,另一个顶点的出度与入度之差为-1,则图D中存在欧拉道路

一些推论:

  • 当D除出、入度之差为1,-1的两个顶点之外,其余顶点的出度与入度都相等时,D的有向欧拉通路必以出、入度之差为1的顶点作为始点,以出、入度之差为-1的顶点作为终点。
  • 当D的所有顶点的出、入度都相等时,D中存在有向欧拉回路。

判断

根据定理和推论,我们可以很好的找到欧拉通路回路的判断方法,这里就给出简明的判断方法:

A.判断欧拉通路是否存在的方法

有向图:图连通,有一个结点出度比入度大1,有一个结点入度比出度大1,其余都是出度=入度。

无向图:图连通,只有两个顶点是奇数度,其余都是偶数度的。

B.判断欧拉回路是否存在的方法

有向图:图连通,所有的顶点出度=入度。

无向图:图连通,所有顶点都是偶数度。

欧拉回路的求解

fleury算法

设G为一无向欧拉图,求G中的一条欧拉回路的算法为:
1)任取G中的一个顶点v0,令P0=v0
2)假设沿Pi=v0e1v1e2…eivi走到顶点vi,按下面的方法从E(G)-{e1,e2,…,ei}中选ei+1
a)ei+1与vi相关联;
b)除非无别的边可供选择,否则ei+1不应该是Gi=G-{e1,e2,…,ei}中的桥。
3)当2)不能再进行时算法停止。
可以证明的是,当算法停止时,所得到的简单回路Pm=v0e1v1e2…emv0为G中的一条欧拉回路。
桥:
设无向图G(V,E)为连通图,若边集E1⊆E,在图G中删除E1中所有边后得到的子图是不连通的,而删除了E1的任一真子集后得到的子图是连通图,则称E1是G的一个割边集。若一条边构成一个割边集,则称该边为割边,或桥

例题

积木/连接问题
题目描述

一天,小明买了许多积木回家,他想把这些积木拼接在一起。每块积木有两个接口,每个接口用一个数字标记,规定只有当两块积木有相同数字标记的接口时,这两块积木才可以通过该接口拼接在一起。例如,有两块积木,接口数字分别为1,2和3,4,那么这两块积木无法拼接;若两块积木接口数字分别为1,2和2,3,那么这两块积木可以通过由数字2标记的接口拼接在一起。现在小明知道所有积木的数量和每块积木接口的数字标记,你能告诉他他可以将所有积木拼接成一个整体么?

输入格式
第一行,一个正整数 n,代表积木数量。
接下来 n 行,每行两个正整数 ai, bi,代表第 i 个积木两端开口的数值。
输出结果
输出 n 个整数表示可以所有积木可以连接在一起的方案,其中第 i 个整数表示排列在第 i 位的积木编号。(注可能有多种方案,只输出一个即可
分析
看到此题目,很自然的想法是将每一个积木视为一个节点,如果两个积木之间接口可以连接的话,可以视为这两个积木之间存在着一条路径,但是,这样抽象之后,题中所要求在构建的图中就应该是可以遍历所有节点的一条道路,但是在不断连接的过程中,会出现许多麻烦,比如,(2 5),(4 2),(5 3),(4 4),(4 4)这五个积木,括号内为对应接口数值,显然因为积木(4 4)有两个,对它的处理就比较困难,所以这种建模仍旧很难求。
仍旧是这组数据:(2 5),(4 2),(5 3),(4 4),(4 4)这五个积木,括号内为对应接口数值,显然五个积木可以拼接到一块儿,拼接方式为:
(3 5)(5 2)(2 4)(4 4)(4 4)
上面我们将各个积木视为节点,能够连接视为存在一条边,但是并不有效,那么不妨换个思路,将各个积木视为边,这么一来,积木两边接口之间的数字才是节点,顺着这个思路下去,只要这个存在接口值为(a b)的积木,a,b两个节点就存在一条边,这样一来,题中所求的就是可以遍历图中所有边的一条欧拉路径,而且一个积木用过后只需对此边进行标记后。
将积木视为边后所建的图
之后按照fleury算法即可求出其中的一条欧拉道路,相应的c++实现如下:

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n, ma = 0, mi = 1000;
int a[150][150], du[150], sta[1500];//a存贮图的邻接矩阵,du存储每个节点的度,sta存储最后的顺序
int top = 0;
struct node{
	int anum,bnum;
	bool visited;
}edge[1500];

void dfs(int x)//dfs寻找一欧拉道路
{
	for (int i = mi; i <= ma; i++)
		if (a[i][x])
		{
			a[i][x]--;
			a[x][i]--;
			dfs(i);
		}
	sta[++top] = x;
}
int main()
{
	int x, y;
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> x >> y;
		du[x]++;
		du[y]++;//统计每个节点的出现次数
		a[x][y]++;
		a[y][x]++;//构建邻接矩阵来存储图
		edge[i].anum = max(x, y);
		edge[i].bnum = min(x, y);
		edge[i].visited = false;
		ma = max(ma, max(x, y));
		mi = min(mi, min(x, y));
	}
	int startnode = 0;
	for (int i = mi; i <= ma; i++)
		if (du[i] & 1)//寻找度为奇数的点,根据定理,这个一定是欧拉道路的起点
		{
			startnode = i;
			break;
		}
	if (!startnode)//st为假,找不到奇数度的点,则任取一个点求图的欧拉回路
	{
		for (int i = mi; i <= ma; i++)
		{
			dfs(i);
			if (sta[1] == sta[top]) break;
		}
	}
	else//st为真,求欧拉道路
	{
		dfs(startnode);
	}
	//求出图的一条欧拉路径后,转化成边的形式输出
	for (int i = n; i > 0; i--){
		node path;
		path.anum = max(sta[i], sta[i + 1]);
		path.bnum = min(sta[i], sta[i + 1]);
		for (int j = 1; j <= n; j++){
			if ((!edge[j].visited)&&(path.anum==edge[j].anum)&&(path.bnum==edge[j].bnum)){
				cout << j << ' ';
				edge[j].visited = true;
				break;
			}
		}
	}
	//system("pause");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值