18.1 欧拉回路存在性的判断
欧拉回路问题可以分为无向图中的欧拉回路和欧拉通路,有向图中的欧拉回路和欧拉通路。这几个问题大抵相像。
有向欧拉回路有:
定理:假设有像多重图D有性质:当忽略有向边上的方向时,得到的图是连通的,那么D有有向欧拉回路当且仅当D的每个顶点的入度和出度相等。
类似的,对有向欧拉通路有:
定理:D有有向欧拉通路,当且仅当除两个不同顶点B和C之外,D的其它顶点的入度和出度相等,且B的出度比入度大1,C的入度比出度大1。在这种情况下,有向欧拉通路自B出发,至C终止。
由上面的定理可以知道,如果要判断一个有向图的欧拉回路是否存在,需要先判断连通性,再判断出度入度。对于无向图,判断方法类似。
判断连通性可以通过DFS或者并查集来实现。
18.2 欧拉回路的构建
在构建欧拉回路之前需要判断欧拉回路是否存在。
构建欧拉回路可以使用Fleury算法。Fleury算法要求能不走桥就不走桥:
1. 任取v0∈V(G),令P0=v0。
2. 设Pi=v0e1v1e2…eivi已经行遍,按下面方法来从E(G)-{e1,e2,…,ei}中选取ei+1:
a) ei+1与vi相关联;
b) 除非无别的边可供行遍,否则ei+1不应该为Gi=G-{e1,e2,…,ei}中的桥。
3. 当2.不能再进行时,算法停止。
构建欧拉回路的Fleury算法可以实用DFS来实现。
《离散数学》中还介绍了另一种构建欧拉回路的算法,就是任意遍历图,直到形成回路,在回路中选择还与不在回路中的点连接的任意点作为起点,再次遍历余下的图,直到形成回路,将找到的新回路加入原来的回路中。如此反复,直到所有的点都已经包含在回路中。这种算法相比Fleury算法较难控制回路的生成,例如下面的例子似乎就不适合用这种方法解决。
18.2.1 实例
PKU JudgeOnline, 2337, Catenyms.
18.2.2 问题描述
给出一些字符串,判断是否能形成欧拉回路。如果不能,输出“***”。否则输出字典序最小的回路。
18.2.3 输入
2
6
aloha
arachnid
dog
gopher
rat
tiger
3
oak
maple
elm
18.2.4 输出
aloha.arachnid.dog.gopher.rat.tiger
***
18.2.5 分析
在没有特殊要求的情况下,DFS遍历图的结点顺序是可以任选的。但是这里由于加上了字典序最小的要求,所以DFS遍历时需要按照以下的优先顺序:
1. 如果有不是桥的边,遍历这些边中字典序最小的边。
2. 否则,遍历这些这些桥中字典序最小的边。
18.2.6 程序
#include<stdio.h> #include<string.h> #include<iostream> using namespace std; #define maxNum 50002 int p[maxNum]; int rank[maxNum]; void makeSet(int x) { p[x] = x; rank[x] = 0; } int findSet(int x) { if(x != p[x]) { p[x] = findSet(p[x]); } return p[x]; } void link(int x, int y) { if(rank[x] > rank[y]){ p[y] = x; }else{ p[x] = y; if(rank[x] == rank[y]) { rank[y] = rank[y] + 1; } } } void unionSet(int x, int y) { link(findSet(x), findSet(y)); } struct sequence{ int next; int word; }; struct word{ char str[22]; int first; int last; int visited; }; int cmp(const void *p1, const void *p2) { return strcmp((*(word*)p1).str,(*(word*)p2).str); } word str1[1002]; int visited[1002]; int N; int buff[1002]; int buffTop; int in[26]; int out[26]; void DFS_Visit(int a) { int i; visited[a] = 1; in[str1[a].last]--; for(i = 0; i < N; i++){ if((visited[i] != 1)&& (str1[a].last == str1[i].first)) { if(in[str1[i].first] != 1){ DFS_Visit(i); } } } for(i = 0; i < N; i++){ if((visited[i] != 1)&& (str1[a].last == str1[i].first)) { DFS_Visit(i); } } buff[buffTop++] = a; } int main() { int i; int from, to; int cases; int length; int fail; int j; int inNode; int outNode; scanf("%d", &cases); for(; cases > 0; cases--){ scanf("%d", &N); for(i = 0; i < 26; i++){ in[i] = 0; out[i] = 0; makeSet(i); } for(i = 0; i < N; i++){ scanf("%s", str1[i].str); length = strlen(str1[i].str); str1[i].visited = 0; str1[i].first = str1[i].str[0] - 'a'; str1[i].last = str1[i].str[length - 1] - 'a'; from = str1[i].first; to = str1[i].last; out[from]++; in[to]++; if(findSet(from) != findSet(to)) unionSet(from, to); } fail = 0; for(i = 0; i < 26; i++){ if(in[i] + out[i] == 0) { continue; } for(j = 0; j < 26; j++){ if(in[j] + out[j] == 0) { continue; } if(findSet(i) != findSet(j)) { fail = 1; break; } } if(fail == 1) { break; } } inNode = -1; outNode = -1; if(fail == 0) { for(i = 0; i < 26; i++){ if(in[i] == out[i]) { continue; } if(in[i] == out[i] + 1){ if(outNode != -1) { fail = 1; break; } outNode = i; }else if(in[i] == out[i] - 1){ if(inNode != -1) { fail = 1; break; } inNode = i; }else{ fail = 1; break; } } } if((inNode == -1 && outNode != -1)|| (inNode != -1 && outNode == -1)) { fail = 1; } if(fail == 0) { qsort(str1, N, sizeof(str1[0]), cmp); if(inNode != -1) { for(i = 0; i < N; i++){ if(str1[i].first == inNode) { inNode = i; break; } } }else{ inNode = 0; } memset(visited, 0, sizeof(visited)); buffTop = 0; DFS_Visit(inNode); for(i = buffTop - 1; i > 0; i--){ cout << str1[buff[i]].str << "."; } cout << str1[buff[0]].str << endl; }else{ cout << "***" << endl; } } return 1; }18.3 实例
PKU JudgeOnline, 1386, Play on Words.
PKU JudgeOnline, 2337, Catenyms.
PKU JudgeOnline, 2513, Colored Sticks.
本文章欢迎转载,请保留原始博客链接http://blog.youkuaiyun.com/fsdev/article