欧拉路径(回路) //思路+例题(模板)

31 篇文章 0 订阅

欧拉路径&&欧拉回路 //思路+例题(模板)

定义:

如果图G中的一个路径包括每个边恰好一次,则该路径称为欧拉路径(Euler path)。

如果一个回路是欧拉路径,则称为欧拉回路(Euler circuit)。 

具有欧拉回路的图称为欧拉图(简称E图)。具有欧拉路径但不具有欧拉回路的图称为半欧拉图。

                                                                                                                                                  -------离散数学

判断

//对于图来说出度+入度是偶数,使用奇数度的顶点数肯定是偶数。

首先都要满足只有一个联通块。

无向图:

            欧拉路径(满足一条):1.任意的v都是偶数的度  2.有两个v是奇数的度,其他都是偶数的度。

            欧拉回路:任意的v都是偶数的度

有向图

           欧拉路径(满足一条):1:任意的v出度==入度  2.有一个v出度=入度+1作为起点,有一个v入度==出度+1作为终点,其他出度==入度。

            欧拉回路:任意的v出度==入度。

代码实现:

1.先判断是否构成欧拉图。

2.找欧拉路径时候遍历完出边再记录路径,最后逆序输出(算法的主要思想) 具体模拟:https://www.cnblogs.com/acxblog/p/7390301.html

3.勿忘判断是否只有一个联通块,可以使用并查集或直接判断遍历的点数是否达到要求。

4.multiset,删边时方便,logN。

Luogu P2731 骑马修栅栏 Riding the Fences  //欧拉路径(无向图)

题意:求能否不重复的遍历所有无向边。

multiset保证字典序最小

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod = 100000007;
const int max_n = 505;
multiset<int>g[max_n];
multiset<int>::iterator it;
int path[5000];int p;
void dfs(int v){
    while(!g[v].empty()){
        it=g[v].begin();
        int to=*it;
        g[v].erase(it);g[to].erase(g[to].find(v));//遍历到就删除边
        dfs(to);
    }
    path[++p]=v;//记录路径
}
int main(){
    int m;scanf("%d",&m);for(int i=1;i<=m;i++){
        int u,v;scanf("%d%d",&u,&v);g[u].insert(v);g[v].insert(u);
    }
    int odd=0;int st=INT_MAX;
    for(int i=1;i<=500;i++){
        if(g[i].size()%2){odd++;st=min(i,st);}//奇数出边,记录起点。
    }
    if(odd==0) {//找最小的起点
        for(int i=1;i<=500;i++){
            if(g[i].size()){st=min(i,st);}
        }
    }
    if(odd>2){
        puts("-1");
        return 0;
    }
    dfs(st);
    for(int i=p;i>=1;i--)printf("%d\n",path[i]);//显然dfs记录的是逆序的
    return 0;
}

 UVa 10129 Play on Words // 欧拉路径(有向图)

题意:给定n个单词,判断能否将这些单词重排后链接起来,期中链接处单词ai的最后一个字母和a(i+1)的第一个字母要相同。

把每一个单词看成一条边,每一个字母当成一个顶点,判断是否是欧拉路径即可。这里可以不用dfs,维护并查集和出度入度即可。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod = 100000007;
const int max_n = 505;
int fa[50];int in[50],ou[50];
int found(int x){
    if(fa[x]==x)return x;
    return fa[x]=found(fa[x]);
}
int main(){
    int T;scanf("%d",&T);while(T--){
        for(int i=0;i<='z'-'a';i++){fa[i]=i,in[i]=ou[i]=0;}
        int n;scanf("%d",&n);for(int i=1;i<=n;i++){
            string s;cin>>s;int u=s[0]-'a';int v=s[s.length()-1]-'a';
            ou[u]++;in[v]++;
            int fx=found(u),fy=found(v);
            if(fx!=fy)fa[fx]=fy;
        }
        int st=-1,ed=-1,book=0;
        for(int i=0;i<='z'-'a';i++){
            if(in[i]==ou[i])continue;
            if(in[i]==ou[i]+1&&ed==-1){ed=i;continue;}//判断能否作为终点
            if(in[i]==ou[i]-1&&st==-1){st=i;continue;}//判断能否作为起点
            book=1;break;//度数出现问题
        }
        if(book){puts("The door cannot be opened.");continue;}
        int cnt=0;
        for(int i=0;i<='z'-'a';i++){
            if((ou[i]||in[i])&&found(i)==i)cnt++;//找到一个联通块
        }
        if(cnt>1){puts("The door cannot be opened.");continue;}
        puts("Ordering is possible.");
    }
    return 0;
}

CF DIV2 E. Neko and Flashback  //欧拉路径(有向图)+ 离散化

题意:给一个序列ai,一个全排列pi,然后可以求出b'和c'。

           

           现在已知 b'和c',问是否可以组成合法序列ai,如果能,输出任意一个。

这个题是真的有质量... 官方题解:https://codeforces.com/blog/entry/66696

发现b'i c'i只会和api ap(i+1)有关,b'i和c'i并且是肯定是取到其中一个的,表示相邻的关系可以所有的b'i c'i连一条无向边,最后求欧拉路径。这样的想法似乎是没有问题的()。

最后需要离散化一下。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod = 100000007;
const int max_n = 1e5+5;
int b[max_n],c[max_n];
vector<int>v;
multiset<int>g[max_n*2];
vector<int>path;
void dfs(int now){
    while(!g[now].empty()){
        set<int>::iterator it;
        it=g[now].begin();
        int to=*it;
        g[now].erase(it);g[to].erase(g[to].find(now));
        dfs(to);
    }
    path.push_back(now);
}
int main(){
    int n;scanf("%d",&n);
    for(int i=1;i<n;i++)scanf("%d",&b[i]),v.push_back(b[i]);
    for(int i=1;i<n;i++)scanf("%d",&c[i]),v.push_back(c[i]);
    sort(v.begin(),v.end());v.erase(unique(v.begin(),v.end()),v.end());
    for(int i=1;i<n;i++){
        if(b[i]>c[i]){printf("-1");return 0;}
        int idu=lower_bound(v.begin(),v.end(),b[i])-v.begin();
        int idv=lower_bound(v.begin(),v.end(),c[i])-v.begin();
        g[idu].insert(idv); g[idv].insert(idu);
    }
    int odd=0;int st=0;
    for(int i=0;i<(int)v.size();i++){
        if(g[i].size()%2){odd++;st=i;}
    }
    if(odd>2){printf("-1");return 0;}
    dfs(st);
    if((int)path.size()!=n){printf("-1");return 0;}//多于一个联通块
    for(int i=path.size()-1;i>=0;i--) printf("%d ",v[path[i]]);
    return 0;
}

 

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值