欧拉回路

起源——七桥问题:城中有七座桥,每座桥连接两座岛屿,如何不重复地走遍7座桥。
欧拉转换成图问题:能否从无向图中的一个结点出发走出一条道路,每条边恰好经过一次,欧拉道路。一笔画问题。
在欧拉道路中,除了起点跟终点,其他点的度数应该是偶数,七桥问题中存在四个点度数是奇数(奇点)。
充分条件:如果一个无向图是连通的,且最多只有两个奇点,则一定存在欧拉道路。如果是两个奇点,一个起点,一个终点;如果不存在奇点,那么任意点出发,都会回到该点(欧拉回路)。
有向图的结论:最多只有两个点的入度不等于出度,而且必须是其中一个点的出度恰好比入度大一(起点),另一个点的入度比出度大一(终点)。
前提条件:忽略边的方向后,图必须是连通的。

欧拉通路: 通过图中每条边且只通过一次,并且经过每一顶点的通路。
欧拉回路: 通过图中每条边且只通过一次,并且经过每一顶点的回路。

无向图是否具有欧拉通路或回路的判定:
欧拉通路:图连通;图中只有0个或2个度为奇数的节点
欧拉回路:图连通;图中所有节点度均为偶数

有向图是否具有欧拉通路或回路的判定:
欧拉通路:图连通;除2个端点外其余节点入度=出度;1个端点入度比出度大1;一个端点入度比出度小1 或 所有节点入度等于出度
欧拉回路:图连通;所有节点入度等于出度

以下代码适用于欧拉道路和回路。
如果打印欧拉道路,主程序调用时,参数必须是道路的起点。打印顺序是逆序的,真正使用这份代码时,应把printf语句换成push语句,把边 (u,v)压入一个栈中。

void euler(int u){
	for(int v=0;v<n;v++){
		if(G[u][v] && !vis[u][v]){
			vis[u][v] = vis[v][u] =1; //若为有向图 改为vis[u][v]=1; 
			euler(v);
			printf("%d %d\n",u,v);  //s.push(v)
		}
	}
}

以下例题参考:https://blog.csdn.net/u011499425/article/details/52685511
例题:单词(UVa 10129h)
输入n(n<=10000)个单词,是否可以把这些单词排成一个序列,使得每个单词的第一个字母和上一个单词的最后一个字母相同(例如:acm\malform\mouse)。每个单词最多包含1000个小写字母。输入中可以用重复单词

对于该题,可以把单词的首字母和尾字母看成顶点,一个单词,它的首尾字母存在一条有向边。这样,问题即为,每条边恰好经过一次。

因为字母作为顶点,题目里给的最多26个。 连通性的判断可以用bfs或dfs遍历即可,可以从某个入度或出度不为0(即26个字母中在该示例中出现了的字母)的顶点出发进行一次遍历,遍历结束后如果还有“出现了的字母”没被访问到,则说明不连通;或者也可以用dfs进行遍历所有的出现了的字母,然后计数,(相当于之前uva 572 求八连通块的个数)个数大于1则说明不连通。 需要注意,在一些地方,要判断26个字母每个字母是否出现。 在判断入度比出度大1、出度比入度大1后,不要忘了所有的点入度都等于出度也是可以的~

欧拉回路是图上的应用,具体我想了想,发现图有很多种情况,有些点可能自己连自己,图的情况可能会很复杂,具体没仔细去想,以后有机会就再学习学习。

#include<cstdio>
#include<cstring>
using namespace std;
int str[27][27],visit[27],du[27][2];//str为邻接矩阵,记录两个节点是否连通
//du用于记录字母的出度与入度
void read(){ //读取字符串,记录开头与结尾的字母 
	int n;
	char por,tear,temp;
	scanf("%d",&n);
	getchar();
	while(n--){
		scanf("%c",&por);
		du[por-'a'][1]++;
		while(temp=getchar()!='\n'){
			tear = temp;
		}
		du[tear-'a'][0]++;
		str[por-'a'][tear-'a']=1;
	}
} 
void dfs(int s){
	//深搜,找是否有通路
	visit[s]=1;
	for(int i=0;i<26;i++){
		if(str[s][i]&&!visit[i]){
			dfs(i);
		}
	} 
}
int solve(){
	int head=-1,tear =-1;
	for(int i=0,count=0;i<26;i++){
		if(du[i][0]!=du[i][1]){
			if(du[i][0]-du[i][1]>=2 &&du[i][0]-du[i][1]<=-2){
				return 0;
			}else{
				count++;
				if(du[i][1]-du[i][0]==1){
					head=i;
				}else{
					tear=i;
				}
			}
			if(count>2) return 0;//如果出现多个出度与入度相差1的节点,则肯定不能有且只有一条通路 
		}
	}
	if(head==-1&&tear==-1){  //count=0 欧拉回路的情况 
		for(int i=0;i<26;i++){
			if(du[i][0]){
				dfs(i);
				break;
			}
		}
	} 
	else{
		if(head==-1||tear==-1) return 0; //count=1
		else{
			dfs(head);//count = 2 从头开始搜 
		}
	}
	for(int i=0;i<26;i++){
		if((du[i][0]+du[i][1])&&visit[i] || (!du[i][0]+du[i][1])&&!visit[i]);
		//看其是否为通路(即有度的节点必须被访问到) 
		else return 0; 
	} 
	return 1;
}
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		memset(str,0,sizeof(str));
		memset(visit,0,sizeof(visit));
		memset(du,0,sizeof(du));
		read();
		if(solve()) puts("Ording is possible");
		else {
			puts("The door cannot be opened.");
		}
	}
	return 0;
}

之前的欧拉(回)路板子

//不能解决多个回路的情况,只能找出一个回路 , 
//一笔画问题,不重复的走过所有的路径 
#include<iostream>
#include<cstdio>
using namespace std;
int start,n,m,cnt,ol[101];
int du[101],g[101][101];
int read()
{
	int r=0,k=1;
	char c=getchar();
	for(;c<'0'||c>'9';c=getchar())if(c=='-')k=-1;
	for(;c>='0'&&c<='9';c=getchar())r=r*10+c-'0';
	return r*k;
}
void find_ol(int i)
{
	for(int j=1;j<=n;j++)
	{
		if(g[i][j]==1)
		{
			g[i][j]=g[j][i]=0;
			find_ol(j);
		}
	}
	ol[++cnt]=i;
	return;
}
int main()
{
	n=read();
	m=read();
	for(int i=1;i<=m;i++)
	{
		int x,y;
		x=read();y=read();
		g[x][y]=g[y][x]=1;
		du[x]++;du[y]++;
	}
	start=1;
	for(int i=1;i<=n;i++)
	{
		if(du[i]%2==1) start=i;//欧拉通路,不是欧拉回路,如果是回路,那么所有的点的度都是偶数 
	}
	cnt=0;
	find_ol(start);
	for(int i=1;i<=cnt;i++)
	printf("%d ",ol[i]);
	cout<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值