欧拉问题

欧拉环:    经过每条边仅且一次

欧拉路径:图中经过每一条边一次且仅一次的路径

欧拉图:    至少包含一个欧拉环

半欧拉图:没有欧拉环,但至少有一条欧拉路径的图


给定一些边,判断是否能够构成欧拉环或者欧拉路径,需要两个判断条件

1: 该图是否连通

2: 入度和出度或者度数是否符合要求


判断是否连通:

       1:  首先要根据需要建图,无论是单词与单词的首尾相连还是木头与木头

               的对色拼接,都需要把这个单词或者这跟木头看做一条边。

       2: 其次是选择方式进行建图,利用并查集,可以讲每条边连入,使之尽

              可能成为一条边或者一个环。

              明确之后,要选择恰当的方式存储每一条边,以下三种方法:

                                             1>  可以直接使用并查集,前提是给出的边是数字,便于存

                                      储和查找

                           2>  可以使用map 进行对应存储

                           3>  可以使用字典树,进行存储,方便快捷,相对与较复杂的

                                     而言,效率要高于map

       3:确定好边之后,要将它纳入并查集,进行整合。最后,在并查集的father数

             组中判断他们是否属于同一个跟,即他们是否连通。若趋于同一跟,则各边

             相互连通。反之,则相反。


对于度数的要求:

1: 对于无向图而言,每一个节点的度数(出度和入度之和)在欧拉图中全部是

       偶数,若在半欧拉图中则是有两个为奇数(欧拉路径的两个端点)其余为偶数。

2: 对于有向图而言。每一个节点的入度和出度在欧拉图中是相等的,在半欧拉

      图中,则只有一个点,出度-入度=1,同时只有一个点的入度-出度=1,

      其余点则相等。


poj 1386:

               给出一定的单词,要求单词两端相同的字母可以首尾相连,请判断是

        否能讲所有的的单词进行守卫相连。

              这道题,给出的单词具有首尾性,因此所有的边为有向边,判断是否

       存在欧拉图或版欧拉图。

               每个单词是一条有向边,将所有的边都纳入并查集(讲单词的首尾字

       母化为数字),根据存储的‘祖先’和每个节点的度数判断。

#include <math.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#define M 30
int p[M],in[M],out[M];
void init()
{
     int i;
     for(i=0;i<M;i++)
     {
          p[i]=i;
          in[i]=0;
          out[i]=0;
     }
     return ;
}
int find(int k)
{
     if(k!=p[k])
          p[k]=find(p[k]);
     return p[k];
}
void mix(int a,int b)
{
     int x=find(a),y=find(b);
     if(x!=y)
          p[x]=y;
     return ;
}
int main()
{
     int m,t,i;
     scanf("%d",&t);
     while(t--)
     {
          scanf("%d",&m);
          init();
          char data[1005];
          for(i=1;i<=m;i++)
          {
               scanf("%s",data);
               int len=strlen(data);
               int start=data[0]-'a';
               int end=data[len-1]-'a';
               out[start]++;
               in[end]++;
               mix(start,end);     
          }
          int num=0;
          for(i=0;i<M;i++)                //记录入度和出度相差为1 的节点数
          {
               if(in[i]!=out[i])
                    num++;
               if(abs(in[i]-out[i])>1)
                    break;
          }
          if(i<M||(num!=0&&num!=2))       //只能有两个节点入度和出度之差为1或者出度和入度相等
          {
               printf("The door cannot be opened.\n");
               continue;
          }
          int flag=0,p;
          for(i=0;i<M;i++)                  //判断是否连通
          {
               if(in[i]==0&&out[i]==0)      //可能只存在一条边
                    continue;
               if(flag==0)
               {
                    p=find(i);
                    flag=1;
               }
               else if(p!=find(i))          //所有节点的‘祖先’必须相同
                    break;
          }
          if(i<M)
               printf("The door cannot be opened.\n");
          else
               printf("Ordering is possible.\n");
    }
    return 0;
}


poj 2513

                 给出一定数目的木头,但是两端都涂了颜色,要求只有相同颜色

      的两个端才能相连。要求判断这些木头是否能否首尾相连。

                 每个木头有两个单词表示颜色,因此如果用数组进行判断和存储

      必定会超时,此时,可以此阿勇字典树。对每个端头的颜色进行存储,可

      以快捷的找出对应的编号。同时,利用利用并查集将这些木头边进行整合

      ,再进行判断。此时存储过程中,一定要存储度数,以便后续使用。


#include<stdio.h>
#include<math.h>
#include<string.h>
#include<iostream>
using namespace std;
#define M 500010
int degree[M],color=0,father[M];
class Trie{                                   //C++使用类
public:
     int id;
     Trie *next[30];                              
     bool flag;
     Trie()                                    //初始化class
     {
          id=0;
          flag=false;
          memset(next,NULL,sizeof(next));
     }
}root;
int Trie_Node(char *k)                        //建立字典树
{
     int i=0;
     Trie *p=&root;
     while(k[i])
     {
          int h=k[i]-'a';
          if(!p->next[h])
              p->next[h]=new Trie;
          p=p->next[h];
          i++;
     }
     if(p->flag)
          return p->id;                        //返回编号以便整合
     else
     {
          p->flag=true;
          color++;
          p->id=color;
          return color;                    
     }
}
int find(int k)
{
     if(k!=father[k])
          father[k]=find(father[k]);
     return father[k];
}
void mix(int a,int b)
{
     int x=find(a),y=find(b);
     if(x!=y)
          father[x]=y;
}
int main()
{
     int i,j,m;
     char a[20],b[20];
     for(i=0;i<M;i++)
          father[i]=i;
     memset(degree,0,sizeof(degree));
     while(scanf("%s%s",a,b)!=EOF)
     {
          int n1=Trie_Node(a);
          int n2=Trie_Node(b);
          degree[n1]++,degree[n2]++;              //记录度数的变化
          mix(n1,n2);
     }
     int num1=0,p=find(1);
     for(i=1;i<=color;i++)
     {
          if(degree[i]%2==1)                     //记录含有奇数度的节点数
              num1++;
          if(p!=find(i))                         //判断是否属于同一‘祖先’    
               break;
     }
     if(i<=color||num1>2)
          printf("Impossible\n");
     else
          printf("Possible\n");
     return 0;
}


判断(半)欧拉图的存在,最重要的思想就是无论怎样存边,都要利用并查集

判断是否整合,同时对节点的度进行判断。






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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值