欧拉路

本文介绍了欧拉路的概念,包括欧拉回路和欧拉通路,并详细讲解了无向图和有向图中的一笔画问题。通过例题解释了如何判断和求解欧拉回路,强调了在解决此类问题时需要注意的图的性质和优化技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先,欧拉路分为欧拉回路和欧拉通路。

欧拉回路

顾名思义,就是一笔画完了后要回到起点。

无向图

所有点的度数都为偶数。

有向图

所有点的入度等于出度。
(入度:有向图中某点作为图中边的 终点 的次数之和)
(出度:有向图中某点作为图中边的 起点 的次数之和)

欧拉通路

也是一笔画,但不用回到起点。
另外,欧拉回路必定是欧拉通路。

无向图

起点和终点的度数为奇数。
其余点的度数为偶数。

有向图

起点的出度比入度大1。
终点的入度比出度大1。
其余节点的入度等于出度。

一笔画问题

判断一个有(无)向图需要几笔画完。

无向图

度数为奇数的点的个数除以2。
(度数为奇数的点的度数不可能为奇数)

有向图

累加所以入度比出度大的点的入度减出度的差即为答案。
(出度也一样)

例题

(WOJ3853)

欧拉回路
描述

有一天一位灵魂画师画了一张图,现在要你找出欧拉回路,即在图中找一个环使得每条边都在环上出现恰好一次。

一共两个子任务:

这张图是无向图。(50 分)

这张图是有向图。(50 分)

输入

第一行一个整数 tt,表示子任务编号。t∈{1,2}t∈{1,2},如果 t=1t=1 则表示处理无向图的情况,如果 t=2t=2 则表示处理有向图的情况。

第二行两个整数 n,mn,m,表示图的结点数和边数。

接下来 mm行中,第ii行两个整数 vi,uivi,ui,表示第 ii 条边(从 11 开始编号)。保证1≤vi,ui≤n1≤vi,ui≤n。

如果 t=1t=1 则表示vivi到 uiui有一条无向边。

如果 t=2t=2 则表示 vivi到 uiui有一条有向边。

图中可能有重边也可能有自环。

输出

如果不可以一笔画,输出一行 NO。

否则,输出一行 YES,接下来一行输出一组方案。

如果 t=1t=1,输出 mm个整数 p1,p2,…,pmp1,p2,…,pm。令 e=|pi|e=|pi|, 那么 ee 表示经过的第ii 条边的编号。如果 pipi为正数表示从 veve 走到 ueue ,否则表示从 ueue走到 veve。

如果 t=2t=2,输出 mm 个整数 p1,p2,…,pmp1,p2,…,pm。其中 pipi 表示经过的第 ii 条边的编号。

样例输入 1

1
3 3
1 2
2 3
1 3

样例输出 1

YES
1 2 -3

样例输入 2

2
5 6
2 3
2 5
3 4
1 2
4 2
5 1

样例输出 2

YES
4 1 3 5 2 6

提示

1≤n≤10^5
0≤m≤2×10^5
1≤n≤10^5
0≤m≤2×10^5

标签

UOJ117

题解

按上面讲的判断即可。
但需注意:

  1. 这个图可能不联通。
  2. 可能会有孤立点,没有边连向这个点,就相当于我们可以不用考虑这个点,因为欧拉回路是遍历完所有的边,而不是点。
  3. 注意用当前弧优化(int &i=first[x]),不然会Time Out(dfs完了后i就会变成0,所有要先存储一次)。
  4. 用ans数组来存储,而不是遍历到就输出,以免遭遇不测。
  5. 数组开小了也会Wrong Answer

上代码:

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0;char c=getchar();
	while(!isdigit(c))c=getchar();
	while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x;
}
struct edge{
	int u,v,nxt;
}e[400010];
int first[100010],cnt=1;
inline void add(int u,int v){
	e[++cnt].u=u;e[cnt].v=v;
	e[cnt].nxt=first[u];first[u]=cnt;
}
int t,n,m,din[100010],dout[100010];
bool vis[400010];
int ans[400010],tot=0;
void dfs1(int x){
	for(int &i=first[x];i;i=e[i].nxt){
		if(vis[i]||vis[i^1])continue;
		int r=i/2,flag=i%2;
		vis[i]=1;
		dfs1(e[i].v);
		if(flag)ans[++tot]=-r;
		else ans[++tot]=r;
	}
}
void dfs2(int x){
	for(int &i=first[x];i;i=e[i].nxt){
		if(vis[i])continue;
		int r=i-1;
		vis[i]=1;
		dfs2(e[i].v);
		ans[++tot]=r;
	}
}
int main(){
	t=read();n=read();m=read();
	if(t==1){
		for(int i=1;i<=m;i++){
			int u,v;
			u=read();v=read();
			add(u,v);add(v,u);
			din[u]++;din[v]++;
		}
		for(int i=1;i<=n;i++){
			if(din[i]%2==1){
				printf("NO");
				return 0;
			}
		}
		for(int i=1;i<=n;++i){//可能没有这个节点 
			if(first[i]){
				dfs1(i);
				break;
			}
		}
		if(tot!=m){//可能不连通 
			printf("NO");
			return 0;
		}
		printf("YES\n");
		for(int i=m;i>=1;i--){
			printf("%d ",ans[i]);
		}
	}
	else{
		for(int i=1;i<=m;i++){
			int u,v;
			u=read();v=read();
			add(u,v);
			din[v]++;dout[u]++;
		}
		for(int i=1;i<=n;i++){
			if(din[i]!=dout[i]){
				printf("NO");
				return 0;
			}
		}
		for(int i=1;i<=n;++i){//可能没有这个节点 
			if(first[i]){
				dfs2(i);
				break;
			}
		}
		if(tot!=m){//可能不连通 
			printf("NO");
			return 0;
		}
		printf("YES\n");
		for(int i=m;i>=1;i--){
			printf("%d ",ans[i]);
		}
	}
	return 0;
}

。:.゚ヽ(。◕‿◕。)ノ゚.:。+゚

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值