1. 欧拉回路,欧拉通路定义与判断
定义:
欧拉回路:从图的某一个顶点出发,图中每条边走且仅走一次,最后回到出发点;如果这样的回路存在,则称之为欧拉回路。
欧拉路径:从图的某一个顶点出发,图中每条边走且仅走一次,最后到达某一个点;如果这样的路径存在,则称之为欧拉路径。
判断:
无向图欧拉回路判断:所有顶点的度数都为偶数。
有向图欧拉回路判断:所有顶点的出度与入读相等。
无向图欧拉路径判断: 之多有两个顶点的度数为奇数,其他顶点的度数为偶数。
有向图欧拉路径判断: 至多有两个顶点的入度和出度绝对值差1(若有两个这样的顶点,则必须其中一个出度大于入度,另一个入度大于出度),其他顶点的入度与出度相等。
判断有向图和无向图是否存在欧拉回路和欧拉路径非常简单, 就是要注意要用并查集统计图的联通分量个数。保证联通分量的个数为1个上述算法才成立。
算法:
下面给出求欧拉回路(路径)的伪代码:
Procedure Euler-circuit (start);
Begin
For 顶点start的每个邻接点v Do
If 边(start,v)未被标记 Then Begin
将边(start,v)作上标记;
将边(v,start)作上标记; //1
Euler-circuit (v);
将边加入栈;
End;
End;
其中图可以用临界矩阵表示也可以用临界表或者边表表示。最后依次去除栈中的边或者顶点(按照具体的需求)得到欧拉回路或欧拉路径(该为代码针对无向图, 如果是有向图就去掉//1行)。如果存在入度和出度差1的顶点(无向图是顶点度数为奇数的顶点) 应该从出度比入度大1的顶点开始搜索。
接下来用Poj 2337 为例子 给出实现代码:
题目大意:输入n个单词,每个单词都形成一条从该单词首字母到尾字母的边,单词尾字母要与下一个单词首字母相同,若可以组成这样的路,即可以组成这样一条连着的单词串,输出路径(单词串),若有多条,则要按字典顺序输出,找不到路则输出***。
代码:
#include <cstdio>
#include <string.h>
#include <algorithm>
using namespace std;
const int MAX_N = 1010;
const int MAX_VERT = 27;
struct Edge{
int v, next;
bool vis;
char str[MAX_VERT];
};
struct Dict{
char str[MAX_VERT];
};
Dict dict[MAX_N];
Edge edge[MAX_N * MAX_N / 2];
int adj[MAX_VERT];
char res[MAX_N][MAX_VERT];
int pre[MAX_VERT], rank[MAX_VERT];
int in[MAX_VERT], out[MAX_VERT];
bool used[MAX_VERT];
int n, edge_num;
int res_cnt;
bool cmp(const Dict& a, const Dict& b)
{
return strcmp(a.str, b.str) >= 0;
}
void add_edge(int u, int v)
{
edge[edge_num].vis = false;
edge[edge_num].v = v;
edge[edge_num].next = adj[u];
adj[u] = edge_num++;
}
void init()
{
for(size_t i = 0; i < MAX_VERT; ++i)
{
pre[i] = i;
rank[i] = 0;
}
}
int find(int x)
{
return x == pre[x] ? x : pre[x] = find(pre[x]);
}
void Union(int x, int y)
{
x = find(x);
y = find(y);
if(x == y)
return;
if(rank[x] > rank[y])
{
pre[y] = x;
}
else
{
if(rank[x] == rank[y])
rank[y]++;
pre[x] = y;
}
}
//judge Euler loop and Euler path in direction graph
//if the out and in degree are same. There exist a Euler loop and return any vertex as start
//if there have two vertex out and in degree not same. And one's out degree minus in degree equal 1
//and another vertex's in degree minus in degress equal 1. Else return -1 represent no Euler loop or path exist
int judge_euler()
{
int comp_num = 0;
int in_cnt = 0, out_cnt = 0;
int res_idx = -1;
for(size_t i = 0; i < MAX_VERT; ++i)
{
if(used[i])
{
if(i == find(i))//count the number of the components
comp_num++;
if(in[i] != out[i])
{
if(in[i] - out[i] == 1)
in_cnt++;
else if(out[i] - in[i] == 1)
{
out_cnt++;
res_idx = i;
}
else
return -1;
}
}
}
if(comp_num != 1)//if there have more or less than 1 component return fail
return -1;
if(!((in_cnt == 1 && out_cnt == 1) || (in_cnt == 0 && out_cnt == 0)))
return -1;
if(res_idx == -1)
{
for(size_t i = 0; i < MAX_VERT; ++i)
{
if(out[i] > 0)
{
res_idx = i;
break;
}
}
}
return res_idx;
}
//calculate Euler path
void euler(int now, int idx)
{
for(int i = adj[now]; i != -1; i = edge[i].next)
{
if(!edge[i].vis)
{
edge[i].vis = true;
euler(edge[i].v, i);
}
}
if(idx != -1)
strcpy(res[res_cnt++], dict[idx].str);
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
memset(in, 0, sizeof(in));
memset(out, 0, sizeof(out));
memset(adj, -1, sizeof(adj));
memset(used, false, sizeof(used));
init();
scanf("%d", &n);
for(size_t i = 0; i < n; ++i)
{
char tmp[MAX_VERT];
scanf("%s", &dict[i].str);
}
sort(dict, dict + n, cmp);
edge_num = 0;
res_cnt = 0;
for(size_t i = 0; i < n; ++i)
{
int u = dict[i].str[0] - 'a';
int v = dict[i].str[strlen(dict[i].str) - 1] - 'a';
add_edge(u, v);
used[u] = used[v] = true;
in[v]++;
out[u]++;
int x = find(u);
int y = find(v);
if(x != y)
Union(x, y);
}
int start = judge_euler();
if(start != -1)
{
euler(start, -1);
for(int i = res_cnt - 1; i >= 0; --i)
{
printf("%s", res[i]);
if(i != 0)
printf(".");
}
printf("\n");
}
else
printf("***\n");
}
}