欧拉回路

欧拉路径:若图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\n", u, v);
    }
} 

//注:G[u][v]为 1 时, 为u和v是连通的, 为 0 不连通 。
//程序只适用于欧拉道路和欧拉回路, 但是如果要打印的是欧拉道路
//在主程序中调用时,参数必须是道路的起点, 另外打印的顺序是逆序的
//因此在真正使用这份代码时应当把printf改成push语句,把(u,v)压入栈 

《1》单词

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=105&page=show_problem&problem=1070

分析一下:

本题主要是把字母看作结点,单词看成有向边,则有解当且仅当图中有欧拉路径。注意要先判连通。 连通如何判断呢? 好无疑问, 并查集。 不用理我, 下面我在吐槽! 哎!感觉自己也不是十分的是个十足的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.\n");
47     else printf("The door cannot be opened.\n");
48   }
49   return 0;
50 }
View Code

代码实现思路:

代码的难度主要在求连通块的部分。 现在我谈一下我对这一部分的理解。

首先是连通块最多有 26 个(至于为什么, 很简单, 因为一共有26个小写字母)。合并能合并的连通块, 减去没出现的字母, 就是结果连通块啦。如果唯一(链)或为0(环)就成功啦! 里面牵扯到好多技巧, 请细看代码!

 

posted @ 2015-06-09 22:15  草滩小恪  阅读(240)  评论(0编辑  收藏  举报