欧拉环: 经过每条边仅且一次
欧拉路径:图中经过每一条边一次且仅一次的路径
欧拉图: 至少包含一个欧拉环
半欧拉图:没有欧拉环,但至少有一条欧拉路径的图
给定一些边,判断是否能够构成欧拉环或者欧拉路径,需要两个判断条件
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;
}
判断(半)欧拉图的存在,最重要的思想就是无论怎样存边,都要利用并查集
判断是否整合,同时对节点的度进行判断。