欧拉回路:图G,若存在一条路,经过G中每条边有且仅有一次,称这条路为欧拉路,如果存在一条回路经过G每条边有且仅有一次,
称这条回路为欧拉回路。具有欧拉回路的图成为欧拉图。
欧拉定理;有向图:图连通,有一个顶点出度大入度1,有一个顶点入度大出度1,其余都是出度=入度。
无向图:图连通,只有两个顶点是奇数度,其余都是偶数度的。
欧拉回路的判断方法
1.先用并查集判断是否相联通
2.再根据欧拉定理判断是否形成一个欧拉回路
例题:poj 1386 Play on Words
题目的大概意思给出一些单词,如果一个单词的尾字母和另一个单词的首字母相同那么就可以连在一起,问这些单词能不能链接到一起
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int max_len = 1000 + 10;
const int maxv = 27;
int in[maxv]; /* 入度 */
int out[maxv]; /* 出度 */
int pai[maxv];/* 并查集数组*/
int ran[maxv];/*并查集深度数组*/
bool used[maxv];/* 标识字符是否出现在图中 */
void init() //初始化数组
{
for(int i = 0; i < maxv; ++i)
{
in[i] = 0;
out[i] = 0;
pai[i] = i;
ran[i]=1;
used[i] = 0;
}
}
int find_set(int x)
{
if(pai[x]!=x)
return pai[x]=find_set(pai[x]);
return x;
}
void union_set(int a, int b)
{
int r1 = find_set(a);
int r2 = find_set(b);
if(r1 == r2)
return;
if(ran[r1]>ran[r2])
{
pai[r2]=r1;
ran[r1] +=ran[r2] ;
}
else
{
pai[r1] = r2;
ran[r2] += ran[r1];
}
}
int main()
{
int t = 0;
int n = 0;
int len = 0;
int s = 0;
int e = 0;
int i = 0;
char word[max_len];
scanf("%d", &t);
while(t--)
{
init();
scanf("%d", &n);
for(i = 0; i < n; ++i)
{
scanf("%s", word);
len = strlen(word);
s = word[0] - 'a';
e = word[len - 1] - 'a';
used[s] = 1;
used[e] = 1;
out[s]++;
in[e]++;
union_set(s, e);
}
//根据并查集判断图是否连通
int scc = 0;
for(i = 0; i < maxv; ++i)
{
if(used[i] && pai[i]==i)
++scc;
}
if(1 < scc)
{
printf("The door cannot be opened.\n");
continue;
}
//入度是否等于出度
int a = 0;
int b = 0;
for(i = 0; i < maxv; ++i)
{
if(used[i] && in[i] != out[i])
{
if(1 == (in[i] - out[i]))
++a;
else if(1 == (out[i] - in[i]))
++b;
else
break;
}
}
if(i < maxv)
printf("The door cannot be opened.\n");
else if(0 == (a + b) || (1 == a && 1 == b))
printf("Ordering is possible.\n");
else
printf("The door cannot be opened.\n");
}
return 0;
}
判断欧拉回路存在之后可以用dfs和佛罗莱算法生成欧拉回路
例题; poj 2230
题意:给出一些点,要求遍历链接这些点的边俩次,最后输出入境
#include <iostream>
#include <stdio.h>
using namespace std;
const int maxm = 2*50000 + 1;
const int maxv = 10000 + 5;
struct edge{
int to;
int next;
};
edge node[maxm]; /*邻接表*/
int adj[maxv]; /*用来记录每个点可以连接的边*/
bool used[maxm];/* 标记边是否访问过*/
void Euler(int vertix)
{
for(int i = adj[vertix]; i != -1; i = node[i].next) //遍历每个点可以出去的边,每个点的最后一个边定义为-1
{
if(!used[i])//看看这条边走过了吗
{
used[i] = 1;
Euler(node[i].to);//先走权值最大的边
}
}
printf("%d\n", vertix);
}
int main()
{
freopen("in.txt","r",stdin);
int n = 0;
int m = 0;
int i = 0;
int u = 0;
int v = 0;
int cnt = 0;
scanf("%d%d", &n, &m);
for(i = 0; i <= n; ++i)
adj[i] = -1;
for(i = 0; i <= m*2; ++i)
used[i] = 0;
for(i = 0; i < m; ++i)
{
scanf("%d%d", &u, &v);
//u->v
node[cnt].to = v;
node[cnt].next = adj[u];//看看这个边的对应边是那一条
adj[u] = cnt++; //u这个点对应的是那几条边
//v->u
node[cnt].to = u;
node[cnt].next = adj[v];
adj[v] = cnt++;
}
Euler(1);
return 0;
}