UOJ #117. 欧拉回路 (图论基础/欧拉回路)

题目

时间限制:1s 空间限制:256MB

有一天,一位灵魂画师画了一张n个点m条边(1≤n≤1e5,0≤m≤2e5)的图。

现在要你找出欧拉回路,即在图中找一个环使得每条边都在环上出现恰好一次。

一共两个子任务:

  1. 这张图是无向图。(50分)
  2. 这张图是有向图。(50分)

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

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

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

  1. 如果 t=1,输出 m 个整数 p1,p2,…,pm。令 e=∣pi∣,那么 e 表示经过的第 i 条边的编号。如果 pi为正数表示从 ve 走到 ue,否则表示从 ue 走到 ve。
  2. 如果 t=2,输出 m 个整数 p1,p2,…,pm。其中 pi 表示经过的第 i 条边的编号。

思路来源

https://www.cnblogs.com/qwerta/p/9732540.html

题解

经典Hierholzer算法,复杂度O(E)

有向图欧拉回路:图连通,一个环的情形(所有点入度出度相等),找环上一点输出路径

有向图欧拉路径:图连通,一个环或一条链的情形(所有点入度出度相等,或仅有恰有两个点,其中一个入度=出度+1,另一个出度=入度+1),找环上一点或链的起点输出路径

无向图欧拉回路:图连通,一个环的情形(所有点度都为偶数),找环上一点输出路径

无向图欧拉路径:图连通,一个环或一条链的情形(所有点度都为偶数,或仅有恰有两个度数为奇数的点),找环上一点或链的一端输出路径

心得

为什么欧拉回路后序输出?(即如下代码,搜完再输出)

void dfs(int u)
{
        ...
	dfs(v);
	printf("%d %d\n",v,u);
}

考虑1->2->3->2->1的情形,

如若前序,则可能会出现1->2,2->1,2->3,3->2的情况,不连续,故后序

此时a->b->c按v->u输出,会被输出为c->b->a,

如最终需输出a->b->c可将u->v压栈,倒序输出

①注意特判m=0,即没有边的情形

②注意弧优化,如若写成for(i=0;i<e[u].size();i++),会被以下样例卡成O(E*E)

1 200000
1 1
1 1
1 1 
1 1
...

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<set>
using namespace std;
typedef pair<int,int> P;
const int N=1e5+10,M=2e5+10;
vector<P>e[N];
bool vis[M],used[N];
int now[N],ans[M],in[N],out[N],cnt;
int op,n,m,u,v;
void dfs(int u)
{
	used[u]=1;
	while(now[u]<e[u].size())//弧优化 也可用前向星实时改head[x] while(head[x]!=-1) 
	{
		int v=e[u][now[u]].first,id=e[u][now[u]].second,fid=abs(id);
		now[u]++;
		if(vis[fid])continue;
		vis[fid]=1;
		dfs(v);
		ans[++cnt]=id;
	}
}
bool ok()
{
	if(m==0)return 1;//特判没边的情形 
	int pos=1;
	if(op==2)
	{
		for(int i=1;i<=n;++i)
		if(in[i]!=out[i])return 0;
		else if(in[i])pos=i;
	}
	else
	{
		for(int i=1;i<=n;++i)
		if((in[i]+out[i])%2)return 0;
		else if(in[i]+out[i])pos=i;
	}
	dfs(pos);
	for(int i=1;i<=n;++i)
	if((in[i]||out[i])&&!used[i])return 0;
	return 1;
}
int main()
{
	scanf("%d",&op);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i)
	{
		scanf("%d%d",&u,&v);
		e[u].push_back(P(v,i));
		if(op==1)e[v].push_back(P(u,-i));
		in[v]++;out[u]++;
	}
	if(!ok())puts("NO");
	else 
	{
		puts("YES");
		for(int i=cnt;i>=1;--i)
		{
			if(op==2)ans[i]=abs(ans[i]);
			printf("%d%c",ans[i],i==1?'\n':' '); 
		}
	}
	return 0;
} 
发布了467 篇原创文章 · 获赞 53 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/Code92007/article/details/104088421