欧拉路径和欧拉回路

 欧拉路径:在一个连通图里面每条边都只走一次且走完所有边的路径。

欧拉回路:在一个连通图里面从一个起点出发,每条边只走一次并且最后回到终点的一个回路。

欧拉回路是包含在欧拉路径里面的,只要欧拉路径的起点和终点是同一个点就是欧拉回路。

在一个欧拉路径里面,要有起点和终点,现在先看起点和终点是不同点的情形:

起点:起点最开始会有一条边1用于走出去,如果后面又通过一条边2走了回来,那么一定会通过一条边3走出去,由此可得,起点所连的边的数量一定是奇数。

终点:终点在最后会有一条没走过的边n可以进去,其余情况下进去和出去都要从别的两个条边进出,由此可得,终点连的边的数量也是奇数。

其他:其余非起点终点的点连的边都是偶数条。

欧拉回路:起点终点都是同一个点时就要保证所有点的数量都是偶数。

前提:所有边都是连通的

1.对于无向图

(1)存在欧拉路径的充分必要条件:度数为奇数的点只能有0个或者2个

(1)存在欧拉回路的充分必要条件:度数为奇数的点只能是0个

2.对于有向图

(1)存在欧拉路径的充分必要条件:所有点的入度等于出度;或者起点的的出度比入度多1,终点的入度比出度多1,其余点入度等于出度

(2)存在欧拉回路的充分必要条件:所有点的入度等于出度


1.铲雪车

信息学奥赛一本通(C++版)在线评测系统

思路:这图是有向图 然后每建一条边都是双向建的 所以每建一条边都会使得一个点的入度和出度+1 所以一定存在欧拉路径(欧拉回路)那么直接求距离就好了

#include <bits/stdc++.h>
using namespace std;
int main()
{
    double x1,y1,x2,y2;
    cin>>x1>>y1;
    double sum=0;
    while(cin>>x1>>y1>>x2>>y2)
    {
        double dx=x1-x2;
        double dy=y1-y2;
        sum+=sqrt(dx*dx+dy*dy)*2;
    }
    int minutes=round(sum/1000/20*60);
    int hours=minutes/60;
    minutes%=60;
    printf("%d:%02d\n",hours,minutes);
    return 0;
}

2.欧拉回路

很普通的欧拉回路问题 但是要判断无解

无向图

1.所有点的度数都为偶数

2.所有边都是连通

有向图

1.所有点入度等于出度

2.所有点都是连通

代码细节:

1.由于边是从1开始编号 但是我们写代码的时候是从0开始的 所以 t 要+1

2.由于无向图其实就是特殊的有向图所以算无向图的时候可以直接把入度+出度看成无向图的度

3.由于 i 是引用的,所以修改 i 的值就相当于修改h[u] 所以每一次枚举都只会h[u]的第一条边

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=4e5+10;
int h[N],e[M],ne[M],idx;
int n,m,type;
int din[N],dout[N];
int cnt,ans[M];
bool used[M];
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u)
{
    for(int &i=h[u];~i;)//加了个引用来优化,让i一直等于h[u]
    {
        if(used[i])
        {
            i=ne[i];//删边
            continue;
        }
        used[i]=true;//标记这条边用过了
        if(type==1) used[i^1]=true;//标记反向边也用过了
        int t;
        if(type==1)
        {
            t=i/2+1;
            if(i&1) t=-t;
        }
        else t=i+1;
        int j=e[i];//先得到这条边
        i=ne[i];//删除这条边
        dfs(j);//遍历这条边
        ans[++cnt]=t;//把这个路径存下来
    }
}
int main()
{
    scanf("%d",&type);
   scanf("%d%d",&n,&m);
   memset(h,-1,sizeof h);
   for(int i=0;i<m;i++)
   {
       int a,b;
       scanf("%d%d",&a,&b);
       add(a,b);
       if(type==1) add(b,a);
       dout[a]++,din[b]++;
   }
   if(type==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(h[i]!=-1)//从有边的节点开始遍历
     {
         dfs(i);
         break;
     }
  if(cnt<m)//假如最后的边数不等于m,说明也不存在
  {
      puts("NO");
      return 0;
  }
  puts("YES");
  for(int i=cnt;i;i--) printf("%d ",ans[i]);//输出答案
  puts("");
   return 0;
}

3.骑马修栅栏

1124. 骑马修栅栏 - AcWing题库

显然是无向图的欧拉路径 但是要考虑输出字典序最小的路径

编号从小到大搜索就行了

#include <bits/stdc++.h>
using namespace std;
const int N=510;
int n=500,m;
int g[N][N];
int ans[1100],cnt;
int d[N];
void dfs(int u)
{
    for(int i=1;i<=n;i++)
        if(g[u][i])
        {
            g[u][i]--,g[i][u]--;
            dfs(i);
        }
    ans[++cnt]=u;
}
int main()
{
    cin>>m;
    while(m--)
    {
        int a,b;
        cin>>a>>b;
        g[a][b]++,g[b][a]++;
        d[a]++,d[b]++;
    }
    int start=1;
    while(!d[start]) start++;
    for(int i=1;i<=n;i++)
        if(d[i]%2)
        {
            start=i;
            break;
        }
    dfs(start);
    for(int i=cnt;i;i--)
        printf("%d\n",ans[i]);

    return 0;
}

4.单词游戏

把每个单词首尾看成一条有向边 一共有26个点 n条边 依次走过每一条边 那么就是判是否存在欧拉路径

 这次不bfs 直接并查集

#include <bits/stdc++.h>
using namespace std;
const int N=30;
int n,m;
int p[N];
bool st[N];
int din[N],dout[N];
int find(int x)
{
    if(p[x]!=x) p[x]=find(p[x]);
    return p[x];
}
int main()
{
    char str[1100];
    int t;
    cin>>t;
    while(t--)
    {
        cin>>n;
        memset(din,0,sizeof din);
        memset(dout,0,sizeof dout);
        memset(st,0,sizeof st);
        for(int i=0;i<26;i++)
            p[i]=i;
        for(int i=0;i<n;i++)
        {
            cin>>str;
            int len=strlen(str);
            int a=str[0]-'a',b=str[len-1]-'a';
            st[a]=st[b]=true;
            dout[a]++,din[b]++;
            p[find(a)]=find(b);
        }
        int start=0,ed=0;
        bool success=true;
        for(int i=0;i<26;i++)
        {
            if(din[i]!=dout[i])
            {
                if(din[i]==dout[i]+1)
                    ed++;
                else if(din[i]+1==dout[i])
                    start++;
                else
                {
                    success=false;
                    break;
                }
            }
        }
        if(success&&!(!start&&!ed||start==1&&ed==1))
        {
            success=false;
        }
        int t=-1;
        for(int i=0;i<26;i++)
            if(st[i])
            {
                if(t==-1) t=find(i);
                else if(t!=find(i))
                {
                    success=false;
                    break;
                }
            }
        if(success) puts("Ordering is possible.");
        else puts("The door cannot be opened.");
    }
    return 0;
}
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值