zoukankan      html  css  js  c++  java
  • 欧拉回路

    欧拉路径:若图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》单词

    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.
    ");
    47     else printf("The door cannot be opened.
    ");
    48   }
    49   return 0;
    50 }
    View Code

    代码实现思路:

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

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

  • 相关阅读:
    使用AOP 实现Redis缓存注解,支持SPEL
    springmvc 双亲上下文导致的 No mapping found for HTTP request
    调用iframe 中的js[兼容各种浏览器]
    NIO学习:异步IO实例
    byte数组与对象之间的相互转换
    NIO学习:buffer读入与写出(文件复制示例)
    NIO学习:使用Channel、Buffer写入文件
    使用摘要流获取文件的MD5
    ActiveMQ入门实例
    获取java项目 classpath目录
  • 原文地址:https://www.cnblogs.com/acm1314/p/4561718.html
Copyright © 2011-2022 走看看