欧拉路径的判断与查找

Posted ucprer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了欧拉路径的判断与查找相关的知识,希望对你有一定的参考价值。

判定

首先图必须是联通的,用并查集判即可

无向图欧拉回路:所有点度数都为偶数

无向图欧拉路径:两个点(或0个点)度数为奇数,其余点(或所有点)度数为偶数

有向图欧拉回路:所有点入度=出度

有向图欧拉路径:一个点入度=出度+1,一个点出度=入度+1,其余点(或所有点)入度=出度

查找-Hierholzer算法

//已知存在欧拉路径,找该路径
void dfs(int u){//s1~sn中存储的是欧拉路径上的点序列
    for(int v=1;v<=n;v++){
        if(mp[u][v]>0){
            mp[u][v]--;
            mp[v][u]--;
            dfs(v);
        }
    }
    s[temp--]=u;
}

为什么是对的?

因为找欧拉路径可以通过在图上的两个奇数点之间加一条边转化为求欧拉回路的问题。通过枚举可以发现,存在欧拉回路的图一定是由一些环嵌套而成(可以通过把一些点一分为二近似地看成仙人掌图)

技术图片

将一个环看成主环,要一笔画走完主环,只要在遇到有副环的点u先沿着副环走一圈,再接着沿着主环走就好了

技术图片

Hierholzer算法就是我们上述思路的一种简单的实现方法:

我们将开始dfs的点属于的环视为主环(其实可以有很多种不同的可能性,不一定就是最显然的那个环),在第一次dfs到无法递归下去时,所有被遍历到的点都在主环上,显然应该将最后遍历到的点放在答案数组的最后,然后一边回溯,如果还是无法递归,那接着从后往前存入答案数组,如果可以递归说明这个点上有一个副环,沿着副环递归下去,也是同样的最后遍历到的点放在后面,这样子就倒着模拟了我们之前的算法。

例题:

The Necklace

https://vjudge.net/contest/347059#problem/C

项链的每颗珠子有两面,每面有一种颜色,项链上相邻两颗珠子的相邻面必须是相同的颜色(项链是一个环),给定一些珠子和它们的颜色,问能否用尽所有的珠子来组合成一个项链?

颜色种类=50

将出现的一种颜色视为图上的点,一颗珠子的两个颜色之间有一条边,显然在这样的图上如果存在一条欧拉回路,那么可以组成项链(跑遍所有的边=用尽所有珠子)。

并查集+Hierholzer算法即可。

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e3+5;
int a[maxn],b[maxn];
int mp[55][55];
int du[55];
int s[maxn],tot,temp;
void dfs(int u){
    for(int v=1;v<=50;v++){
        if(mp[u][v]>0){
            mp[u][v]--;
            mp[v][u]--;
            dfs(v);
        }
    }
    s[temp--]=u;
}
int fa[55],vis[55];
int find(int x){
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
int main(){
    int T;
    cin>>T;
    for(int kase=1;kase<=T;kase++){
        int n;
        cin>>n;
        for(int i=1;i<=50;i++)fa[i]=i;
        memset(mp,0,sizeof(mp));
        memset(du,0,sizeof(du));
        memset(s,0,sizeof(s));
        memset(vis,0,sizeof(vis));
        tot=0;
        for(int i=1;i<=n;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            vis[u]=vis[v]=1;
            int fu=find(u),fv=find(v);
            fa[fu]=fv;
            mp[u][v]++;
            mp[v][u]++;
            du[u]++;
            du[v]++;
            tot++;
        }
        int cnt1=0;
        for(int i=1;i<=50;i++){
            if(vis[i]&&fa[i]==i)cnt1++;
        }
        if(cnt1>1){
            printf("Case #%d
some beads may be lost
",kase);
            continue;
        }
        int flag=1;
        for(int i=1;i<=50;i++){
            if(du[i]&1){
                flag=0;
                break;
            }
        }
        if(!flag){
            printf("Case #%d
some beads may be lost
",kase);
            if(kase<=T)printf("
");
            continue;
        }
        temp=tot;
        for(int i=1;i<=50;i++){//找一个点
            if(vis[i]){
                dfs(i);
                break;
            }
        }
        printf("Case #%d
",kase);
        for(int i=1;i<=tot;i++){
            printf("%d %d
",s[i],s[i%tot+1]);
        }
        if(kase<=T)printf("
");
    }
}

以上是关于欧拉路径的判断与查找的主要内容,如果未能解决你的问题,请参考以下文章

欧拉路

欧拉路

hdu 1116 并查集和欧拉路径

[P1341]无序字母对 (欧拉回路)

UOJ 117 欧拉回路(套圈法+欧拉回路路径输出+骚操作)

欧拉回路 && 欧拉路径