【算法】欧拉回路和欧拉路径

欧拉回路和欧拉路径是一种判断一笔画问题的算法,回路是从源点出发后可以遍历过所有的边且经过一次后可以回到原点,路径是从原点出发后遍历所有的边且经过一次后可以到达终点

  • 欧拉回路

实现欧拉回路首先需要判断是否存在欧拉回路。

以上图为例,在因为欧拉回路是重新回到了源点,也说明欧拉回路必须至少有一个环,并且没有一条边会被孤立。所以这也就说明每个点的出度和入度和必须成对,不然绝对会有边遍历不到。

对于无向图来说,也就是度数为偶数。源点的选择也就是它有边连接,因为可能会出现孤立点的情况。

然后就是如何求欧拉回路。其实欧拉回路就是一个爆搜的过程(没有任何技术含量)。

每次枚举每个点的出边,如果这条边没有遍历过就继续搜,无向图需要将正反两条边都干掉,再每次记录走过的边。

还需注意跑完 d f s dfs dfs 后,如果欧拉回路的边的数量没有和所有边的数量一样,也就是说没有遍历所有的边,也不是一个欧拉回路。

#include<bits/stdc++.h>
using namespace std;
struct edge{
	int to,next;
}ed[1000001];
int he[1000001],idx;
int din[1000001],dout[1000001];
int ans[1000001],cnt;
bool vis[1000001];
int n,m,t;
void insert(int u,int v)
{
	ed[idx].to=v;
	ed[idx].next=he[u];
	he[u]=idx++;
}
void dfs(int u)
{
	for(int i=he[u];i!=-1;i=ed[i].next)
	{
		if(vis[i]) continue;
		vis[i]=1;
		if(t==1) vis[i^1]=1;//双向 
		dfs(ed[i].to);
		if(t==1) ans[++cnt]=(i>>1)+1;//点的编号
		else ans[++cnt]=i+1;
	}
}
int main()
{
	memset(he,-1,sizeof(he));
	scanf("%d %d %d",&t,&n,&m);//1是无向图,2是有向图 
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d %d",&x,&y);
		insert(x,y);
		if(t==1) insert(y,x);
		din[y]++;dout[x]++;
	}
	if(t==1)
	{
		for(int i=1;i<=n;i++)
			if((din[i]+dout[i])&1)//度数为偶数 
			{
				puts("NO");
				return 0;
			}
	}
	else
	{
		for(int i=1;i<=n;i++)
			if(din[i]!=dout[i])//出度入度成对 
			{
				puts("NO");
				return 0;
			}
	}
	for(int i=1;i<=n;i++)
		if(he[i]!=-1)//有边连接 
		{
			dfs(i);
			break;
		}
	if(cnt<m)//没有遍历所有的边 
	{
		puts("NO");
		return 0;
	}
	puts("YES");
	for(int i=1;i<=cnt;i++) printf("%d ",ans[i]);
	return 0;
}
  • 欧拉路径

首先还是需要判断这个图是否存在欧拉路径。

因为没有求是否最后需要回到源点。所以对于无向图而言,至多有两个点度数可以不为偶数,并且这两个点分别是源点终点

有向图也是一样,但还需满足源点只出不进,也就是出度比入度大 1 1 1,终点只进不出,也就是入度比出度 1 1 1 。还需要记得,欧拉回路是一种特殊的欧拉路径,所以如果满足欧拉回路的性质,它也必然存在一条欧拉路径。

和欧拉回路十分相近,只是在判别的时候不太一样,实现方法还是 d f s dfs dfs,以下代码以有向图为例不会告诉你我不想写无向图了

#include<bits/stdc++.h>
using namespace std;
struct edge{
    int to,next;
}ed[1000001];
struct node{
    int u,v;
}a[1000001];
int he[1000001],idx=1;
int din[1000001],dout[1000001];
bool vis[1000001];
int ans[1000001],cnt;
int n,m;
void insert(int u,int v)
{
    ed[idx].to=v;
    ed[idx].next=he[u];
    he[u]=idx++;
}
void dfs(int u)//寻找欧拉路径
{
    for(int i=he[u];~i;i=ed[i].next)
        if(!vis[i])
        {
            vis[i]=1;
            dfs(ed[i].to);
        }
    ans[++cnt]=u;
    return ;
}
int main()
{
    memset(he,-1,sizeof(he));
    int s=0,cnta=0,cntb=0;
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++)
    {
    	int x,y;
        scanf("%d %d",&x,&y);
        insert(x,y);
        din[y]++;dout[x]++;
    }
    for(int i=1;i<=n;i++)
    {
        if(din[i]-dout[i]>1||dout[i]-din[i]>1)//有两个以上差相差2的点
        {
            puts("No");
            return 0;
        }
        if(dout[i]-din[i]==1) cnta++,s=i;//找原点
        else if(din[i]-dout[i]==1) cntb++;
    }
    if(!s)//欧拉回路时
    {
        for(int i=1;i<=n;i++)
            if(!(din[i]+dout[i]&1))
            {
                s=i;
                break;
            }
    }
    if((!cnta&&!cntb)||(cnta==1&&cntb==1))//只有原点和终点或为欧拉回路 
    {
        dfs(s);
        for(int i=cnt;i>0;i--) printf("%d ",ans[i]);
    }
    else puts("No");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值