P7771 欧拉路径
2023-08-09 20:32:32 # OI # Problem

题面

这里不放题面了,扔个链接

题意就是找一条字典序最小的欧拉路径(或欧拉回路)。

思路

有向图中找欧拉路径相信大家都会,就是不断递归删边然后回溯的时候将起点压栈就可以了。

然后如何做到字典序最小呢?

我们可以先将图建出来,然后遍历每个点的出边,把每个点的出边按照字典序从小到大排序,这样遍历的时候就可以实现字典序最小了。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=10000005,M=20000005;
int frt[N],nxt[M],to[M],cnt,now[N];
int rd[N],cd[N];
int flg;
int S,T,top;
int stk[M];
void add_edge(int u,int v)
{
	cnt++;
	nxt[cnt]=frt[u];
	rd[v]++;
	cd[u]++;
	frt[u]=cnt;
	to[cnt]=v;
	return;
}
int a[M];
bool cmp(int u,int v)//见过鄙视你按字典序升序排序
{
	return to[u]<to[v];
}
void readd(int u)
{
	int tot=0;
	for(int i=frt[u];i;i=nxt[i]) a[++tot]=i;//将每条边的编号记录在a数组,然后排序后重新建边
	sort(a+1,a+1+tot,cmp);
	frt[u]=0;//现在以u为起点的边都被拆完排好序了
	for(int i=tot;i>=1;i--)
	{
		nxt[a[i]]=frt[u];//从最大的边开始倒着往前建,不断往前加边
		frt[u]=a[i];//第一条边就变成了a[i] 
	}
	return;
}
void dfs(int u)
{
	for(;now[u];)//now[]代表当前所在的边
	{
		int v=to[now[u]];
		now[u]=nxt[now[u]];//先把它去掉 
		dfs(v);
	}
	stk[++top]=u;
	return;
}
signed main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		add_edge(u,v);
	}
	for(int i=1;i<=n;i++)
	{
		readd(i);//重新建边 
		now[i]=frt[i];//重新建边后一开始在看最小的一条边
	}
	for(int i=1;i<=n;i++)
	{
		if(cd[i]>rd[i])//起点 
		{
			if(cd[i]!=rd[i]+1) flg=1;//出度不比入度多1,不合法
			else
			{
				if(S) flg=1;//有多个起点,不合法
				else S=i;//否则就作为起点
			}
		}
		if(rd[i]>cd[i])//终点 
		{
			if(rd[i]!=cd[i]+1) flg=1;//入度不比出度多1,那就不是终点,不合法
			else
			{
				if(T) flg=1;//有多个终点,不合法
				else T=i;//否则就作为终点
			}
		}
	}
	if(flg||!S&&T||!T&&S)//若不合法或者没有起点或者没有终点
	{
		puts("No");
		return 0;
	}
	if(!S) S=1;//如果起点终点都没有,说明每个点的入度都等于出度,那么就可以找欧拉回路,将起点定为1.
	dfs(S);
	for(int i=top;i>=1;i--)
		printf("%d ",stk[i]);
	return 0;
}