欧拉回路(欧拉路径)

定义

给一个连通图,求一条每条边恰好走一次的路径,就叫欧拉路径,如果要求回到原点,就叫欧拉回路。(也叫一笔画问题)

推论

无向图

把度数为偶数的点叫偶点,度数为奇数的点叫奇点。

  • 如果一个图存在欧拉路径,则奇点的个数一定为0个或2个。
    白话证明:
    • 奇点个数为0个时,所有点都为偶点。
      则可以使得每个点的入边数量与出边数量相等,一定存在一个环。
      走完这个环后,每个点剩余的边一定是偶数,说明从这个点出发一定可以走出一个环再回到这个点,可以将这个新的环与原来的环拼接,形成新的路径,从而遍历所有边。
    • 如果奇点个数为2个,将这两个边连接,形成↑上面的样子,保证最后走新连的边,就可以完成欧拉路径。
  • 只有奇点为0个的图存在欧拉回路,证明同上。
  • 奇点有2个时,必然一个是起点,一个是终点。
有向图
  • 只有每个点的出度等于入度时才存在欧拉回路
    (回路必然有进有出,肯定要相等啊,,,)
  • 如果存在一个点的入度比出度大1,另一个点出度比入度大1,则存在欧拉路径。(证明通无向图)

求法

消圈法

如同前面无向图欧拉回路证明一样,找一个圈,中途把其它的圈并上来,并成一条路径,有递归版本和循环版本。
此算法只能计算确定有欧拉路径的图

代码
递归
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=505,MAXE=1100;
int E[MAXN][MAXN];
int ans[MAXE],cnt;
int deg[MAXN],n,m,S,T;
void euler(int id)
{
    for(int i=1;i<=n;i++)
        if(E[id][i]>0)
        {
            E[id][i]--;
            E[i][id]--;
            euler(i);
        }
    ans[++cnt]=id;
}
int main()
{
    int a,b;
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&a,&b);
        E[a][b]++;
        E[b][a]++;
        deg[a]++;
        deg[b]++;
        n=max(n,a);
        n=max(n,b);
    }
    S=0;T=0;
    for(int i=1;i<=n;i++)
        if(deg[i]&1)
        {
            if(S)
            {
                T=i;
                break;
            }
            S=i;
        }
    if(S)
        euler(S);
    else
        euler(1);
    for(int i=cnt;i>=1;i--)
        printf("%d\n",ans[i]);
    return 0;
}
循环
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=505,MAXE=1100;
int E[MAXN][MAXN];
int ans[MAXE],cnt;
int deg[MAXN],n,m;
int stack[MAXE],top;
void find_circle(int S)
{
    int x=S;
    bool flag=true;
    while(flag)
    {
        stack[++top]=x;
        flag=false;
        for(int i=1;i<=n;i++)
            if(E[x][i]>0)
            {
                E[x][i]--;
                E[i][x]--;
                x=i;
                flag=true;
                break;
            }
    }
    if(stack[top]==S)
        top--;
}
void euler(int S)
{
    find_circle(S);
    while(top)
    {
        ans[++cnt]=stack[top];
        top--;
        find_circle(stack[top+1]);
    }
}
int main()
{
    int a,b,S,T;
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&a,&b);
        E[a][b]++;
        E[b][a]++;
        deg[a]++;
        deg[b]++;
        n=max(n,a);
        n=max(n,b);
    }
    S=0;T=0;
    for(int i=1;i<=n;i++)
        if(deg[i]&1)
        {
            if(S)
            {
                T=i;
                break;
            }
            S=i;
        }
    if(S)
        euler(S);
    else
        euler(1);
    for(int i=cnt;i>=1;i--)
        printf("%d\n",ans[i]);
    if(!S)
        printf("1\n");
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值