所谓的欧拉图,就是我们小时候经常玩的一种"一笔画"游戏,从任意一个点出发,经过所有的边。而我们将这个模型抽象出来,对边和起点加了不同的限制,就得到了不同的定义。
欧拉通路: 通过图中每条边且只通过一次,并且经过每一顶点的通路。欧拉回路: 通过图中每条边且只通过一次,并且经过每一顶点的回路。
欧拉回路相关定理:
1、无向图为欧拉图(欧拉回路),当且仅当为连通图且所有顶点的度为偶数。
2、无向图为半欧拉图(欧拉通路),当且仅当为连通图且除了两个顶点的度为奇数之外,其它所有顶点的度为偶数。
3、有向图为欧拉图(欧拉回路),当且仅当的基图连通,且所有顶点的入度等于出度。(忽略有向图所有边的方向,得到的无向图称为该有向图的基图。)
4、有向图为半欧拉图(欧拉通路),当且仅当的基图连通,且存在顶点的入度比出度大1、的入度比出度小1,其它所有顶点的入度等于出度。
求无向图的欧拉回路:
Hdu1878
思路::1、底图是连通的,可用并查集判断。2、不存在度数为奇数的点。
#include <cstdio>
#include <cstring>
const int N=1005;
int father[N],degree[N];
int find (int x)
{
if (x==father[x]) return x;
return father[x]=find(father[x]);
}
int main ()
{
int n,m,i;
while (scanf("%d",&n),n)
{
scanf("%d",&m);
for (i=1;i<=n;i++)
father[i]=i;
memset(degree,0,sizeof(degree));
while (m--)
{
int a,b;
scanf("%d%d",&a,&b);
father[find(b)]=find(a);
degree[a]++;
degree[b]++;
}
int p=father[1],flag=1;
for (i=1;i<=n;i++)
if (father[i]!=p||degree[i]&1)
{
flag=0;
break;
}
printf("%d\n",flag);
}
return 0;
}
求无向图的欧拉通路:
POJ1300
题意:有n个房间,每个房间有若干个门和别的房间相连,管家从m房间开始走,要回到自己的住处(0),问是否有一条路可以走遍所有的门并且没有重复的路。
思路:如果没有奇数门的房间,起始点为0,则存在欧拉回路。可以回到住处0。如果有2个奇数门的房间,则存在欧拉通路,并且只有房间0和起始点的房门数量为奇数,这样才可能回到住处0。
using namespace std;
typedef long long ll;
int main()
{
char a[M];
int m,n;
while(cin>>a)
{
else cin>>m>>n;
=
/*
读取数据,door[i]表示第i个房间门的数量
*/
for(i=0;i<n;i++)
{
if(door[i]&1) odd++; //看看有多少个点度数是奇点……
}
if(odd==0&&m==0) printf("YES %d\n",num); //如果没有奇点,而起始点是0的可以最后回到0点符合,如果第一个样例
else if(odd==2&&door[m]&1&&door[0]&1&&m!=0) //如果有2个奇点,且这两个起始点是0和另一个,并且起始点是另一个才能回到0点,如第三个样例
printf("YES %d\n",num);
else printf("NO\n"); //第二个样例
}
return 0;
}
求有向图的欧拉通路:
POJ1386
题意:给你一些字符串,字符串末位置如果和另一个字符串的首位置相同的话就可以相连 。然后问你是否可以全部连起来。
思路:就是取出每个字符串的首尾位置的字母作为点,然后求出出度和入度,根据有向欧拉通路的性质,可以求出是否可以组成欧拉通路或者欧拉回路。并查集判断是否是一个连通图。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define clr(a) memset(a , 0 , sizeof(a) )
using namespace std ;
char a[1111] ;
struct kdq{
int s,e;
}al[111111] ;
int in[30] ,out[30];
bool vis[30] ;
int fa[30] ;
int find(int x){
return fa[x] == x ? x : fa[x] = find(fa[x]) ;
}
void Union(int a, int b){
a = find(a) ;
b = find(b) ;
if(a == b)return ;
if(a < b)fa[b] = a ;
else fa[a] = b ;
}
void init(){
for (int i = 0 ; i <= 26; i ++ )fa[i] = i ;
clr(in) ;
clr(out) ;
clr(vis) ;
}
int main(){
int T ;
cin >> T ;
while( T -- ){
int n ;
cin >> n ;
init() ;
for (int i = 1 ; i <= n ; i ++ ){
scanf("%s",a) ;
int l = strlen(a) ;
al[i].s = a[0] - 'a';
al[i].e = a[l - 1] - 'a';
in[a[0] - 'a'] ++ ;
out[a[l - 1] - 'a'] ++ ;
vis[a[0] - 'a'] = 1 ;
vis[a[l - 1] - 'a'] = 1 ;
}
for (int i = 1 ; i <= n ; i ++ ){
int u = al[i].s ;
int v = al[i].e ;
Union(u , v) ;
}
int jihe = -1 ;
bool flag = 0 ;
for (int i = 0 ; i < 26 ; i ++ ){
if(!vis[i])continue ;
if(jihe == -1){
jihe = find(i) ;
}
else if(find(i) != jihe){
flag = 1 ;
break ;
}
}
int num = 0 ;
for (int i = 0 ; i < 26 ;i ++ ){
if(!vis[i])continue ;
if(abs(in[i] - out[i]) >= 2){//如果入度和出度相差2以上,则不可能存在回路或者通路,因为有向图的欧拉通路/回路中没有这样的节点。
flag = 1 ;
break ;
}
else if(abs(in[i] - out[i]) == 1){//如果入度和出度相差1,那么则要记录这样的点有几个。如果1个出度比入度大1,1个出度比入度小1,则构成欧拉通路。
num ++ ;
}
}
if(flag){
puts("The door cannot be opened.") ;
}else
if(num == 0 || num == 2 ){//如果没有入度和初度相差1的节点,则说明全都是偶数度节点,存在欧拉回路。
puts("Ordering is possible.") ;
}else {
puts("The door cannot be opened.") ;
}
}
return 0 ;
}
求有向图的欧拉回路:
哈理工oj 1633
题意:跟上面那道题差不多,不过要求把全部单词连接起来并能形成环状。是上一道题的一个子问题
#include<stdio.h>
#include<string.h>
using namespace std;
int in[105];
int out[105];
int vis[105];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
memset(vis,0,sizeof(vis));
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
char str[15];
scanf("%s",str);
int x=str[0]-'a';
int y=str[strlen(str)-1]-'a';
in[x]++;
out[y]++;
vis[x]=1;
vis[y]=1;
}
int zhongdian=0;
int qidian=0;
int cont=0;
for(int i=0;i<30;i++)
{
if(vis[i]==1)
{
if(in[i]!=out[i])
{
if(in[i]-out[i]==1)
{
zhongdian++;
}
if(out[i]-in[i]==1)
{
qidian++;
}
cont++;
}
}
}
if(cont==0)
{
printf("circle\n");
}
else if(cont==2)
{
if(zhongdian==1&&qidian==1)
{
printf("path\n");
}
else
{
printf("oh no\n");
}
}
else
{
printf("oh no\n");
}
}
}
输出路径:
设G 为一无向欧拉图,求G 中一条欧拉回路的算法为:
1) 任取G 中一顶点v0,令P0 = v0;
2) 假设沿Pi = v0e1v1e2v2 …eivi 走到顶点vi,按下面方法从E(G) - { e1, e2, …, ei }中选ei+1:
a) ei+1 与vi 相关联;
b) 除非无别的边可供选择,否则ei+1 不应该是Gi = G - { e1, e2, …, ei }中的桥。
3) 当2)不能再进行时算法停止。
可以证明的是,当算法停止时,所得到的简单回路Pm = v0e1v1e2v2 …emvm, (vm = v0)为G 中一条欧拉回路
题意:
给出一些字符串,问能否将这些字符串 按照 词语接龙,首尾相接 的规则 使得每个字符串出现一次
如果可以 按字典序输出这个字符串序列
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1000+10;
struct Word
{
int len;
char s[20+5];
bool operator <(const Word &b)const
{
return strcmp(s,b.s)<0;
}
}words[maxn];
struct Edge
{
int u,v;//u表示起点,v表示终点
bool vis;
}edges[maxn];
int fa[26+5]; //并查集
int in[26+5],out[26+5];//入度,出度
bool mark[26+5]; //mark[i]=true,表i这个字母被用到作为图顶点了
int m;//单词数,即边数
int cnt,ans[maxn];//记录输出单词的序号,逆序
int findset(int u)
{
if(fa[u]==-1) return u;
return fa[u]=findset(fa[u]);
}
bool ok()//判断是否弱连通图
{
int k=0;
for(int i=0;i<26;i++)if(mark[i])
if(findset(i)==i) k++;
return k==1;
}
void euler(int u)
{
for(int i=0;i<m;i++)if(!edges[i].vis && edges[i].u==u)//本边未走过且起点吻合
{
edges[i].vis=true;
euler(edges[i].v);
ans[cnt++]=i;
}
}
int main()
{
int T; scanf("%d",&T);
while(T--)
{
scanf("%d",&m);
for(int i=0;i<m;i++)
{
scanf("%s",words[i].s);
words[i].len=strlen(words[i].s);
}
sort(words,words+m);
memset(fa,-1,sizeof(fa));
memset(mark,0,sizeof(mark));
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
for(int i=0;i<m;i++)
{
int u,v;
u=words[i].s[0]-'a';
v=words[i].s[words[i].len-1]-'a';
edges[i].u=u;
edges[i].v=v;
edges[i].vis=false; //表示该边还未被访问
mark[u]=mark[v]=true;
in[v]++, out[u]++;
u=findset(u), v=findset(v);
if(u!=v) fa[u]=v; //合并连通分量
}
int start=edges[0].u;//欧拉路径起始点的编号
int i,c1=0,c2=0;
for(i=0;i<26;i++)
if(mark[i])
{
if(in[i]==out[i]) continue;
else if(in[i]-out[i]==1) c1++;
else if(out[i]-in[i]==1) c2++,start=i;
else break;
}
if(i==26&&((c1==c2&&c1==0)||(c1==c2&&c1==1)) && ok() )//所有点度符合要求且弱连通
{
cnt=0;
euler(start);
printf("%s",words[ans[cnt-1]].s);
for(int i=cnt-2;i>=0;i--)
printf(".%s",words[ans[i]].s);
printf("\n");
}
else printf("***\n");
}
return 0;
}