欧拉路径:若图G中存在这样一条路径,使得它恰通过G中每条边一次,则称该路径为欧拉路径。若该路径是一个圈,则称为欧拉(Euler)回路。
图论起源于18世纪,1736年瑞士数学家欧拉(Euler)发表了图论的第一篇论文“哥尼斯堡七桥问题”。在当时的哥尼斯堡城有一条横贯全市的普雷格尔
河,河中的两个岛与两岸用七座桥连结起来。当时那里的居民热衷于一个难题:有游人怎样不重复地走遍七桥,最后回到出发点。 为了解决这个问题,欧拉用A,B,C,D4个字代替陆地,作为4个顶点,将联结两块陆地的桥用相应的线段表示,于是哥尼斯堡七桥问题就变成了图中,是否存在经过每条边一次且仅一次,经过所有的顶点的回路问题了。欧拉在论文中指出,这样的回路是不存在的。
//欧拉回路 void euler(int u) { for(int v = 0; v<n; v++) if(G[u][v]&&vis[u][v]) { vis[u][v] = vis[v][u] = 1; euler(v); printf("%d %d ", u, v); } } //注:G[u][v]为 1 时, 为u和v是连通的, 为 0 不连通 。 //程序只适用于欧拉道路和欧拉回路, 但是如果要打印的是欧拉道路 //在主程序中调用时,参数必须是道路的起点, 另外打印的顺序是逆序的 //因此在真正使用这份代码时应当把printf改成push语句,把(u,v)压入栈
《1》单词
分析一下:
本题主要是把字母看作结点,单词看成有向边,则有解当且仅当图中有欧拉路径。注意要先判连通。 连通如何判断呢? 好无疑问, 并查集。 不用理我, 下面我在吐槽! 哎!感觉自己也不是十分的是个十足的fool 。 然而现在还是停留在能勉强看懂代码的水平上(要费好大的劲才能看懂!)。 不说啦,都是眼泪!。
1 // 算法:把字母看作结点,单词看成有向边,则有解当且仅当图中有欧拉路径。注意要先判连通 2 #include<cstdio> 3 #include<cstring> 4 #include<vector> 5 using namespace std; 6 7 const int maxn = 1000 + 5; 8 9 // 并查集(代码摘自《算法竞赛入门经典——训练指南》第三章) 10 int pa[256]; 11 int findset(int x) { return pa[x] != x ? pa[x] = findset(pa[x]) : x; } 12 13 int used[256], deg[256]; // 是否出现过;度数 14 15 int main() { 16 int T; 17 scanf("%d", &T); 18 while(T--) { 19 int n; 20 char word[maxn]; 21 22 scanf("%d", &n); 23 memset(used, 0, sizeof(used)); 24 memset(deg, 0, sizeof(deg)); 25 for(int ch = 'a'; ch <= 'z'; ch++) pa[ch] = ch; // 初始化并查集 26 27 int cc = 26; // 连通块个数 (最多有26个连通块) 28 29 for(int i = 0; i < n; i++) { 30 scanf("%s", word); 31 char c1 = word[0], c2 = word[strlen(word)-1]; 32 deg[c1]++; //很机智的方法 33 deg[c2]--; 34 used[c1] = used[c2] = 1; //表示该字母被用过 35 int s1 = findset(c1), s2 = findset(c2); 36 if(s1 != s2) { pa[s1] = s2; cc--; } 37 } 38 39 vector<int> d; 40 for(int ch = 'a'; ch <= 'z'; ch++) { 41 if(!used[ch]) cc--; //没出现过的字母 42 else if(deg[ch] != 0) d.push_back(deg[ch]); 43 } 44 bool ok = false; 45 if(cc == 1 && (d.empty() || (d.size() == 2 && (d[0] == 1 || d[0] == -1)))) ok = true; 46 if(ok) printf("Ordering is possible. "); 47 else printf("The door cannot be opened. "); 48 } 49 return 0; 50 }
代码实现思路:
代码的难度主要在求连通块的部分。 现在我谈一下我对这一部分的理解。
首先是连通块最多有 26 个(至于为什么, 很简单, 因为一共有26个小写字母)。合并能合并的连通块, 减去没出现的字母, 就是结果连通块啦。如果唯一(链)或为0(环)就成功啦! 里面牵扯到好多技巧, 请细看代码!