• 「LuoguP1341」 无序字母对(欧拉回路


    题目描述

    给定n个各不相同的无序字母对(区分大小写,无序即字母对中的两个字母可以位置颠倒)。请构造一个有n+1个字母的字符串使得每个字母对都在这个字符串中出现。

    输入输出格式

    输入格式:

    第一行输入一个正整数n。

    以下n行每行两个字母,表示这两个字母需要相邻。

    输出格式:

    输出满足要求的字符串。

    如果没有满足要求的字符串,请输出“No Solution”。

    如果有多种方案,请输出前面的字母的ASCII编码尽可能小的(字典序最小)的方案

    输入输出样例

    输入样例#1: 复制
    4
    aZ
    tZ
    Xt
    aX
    输出样例#1: 复制
    XaZtX
     

    说明

    【数据规模与约定】

    不同的无序字母对个数有限,n的规模可以通过计算得到。


     题解

    首先翻译一下题面吧。

    给定n条无向边,试构造一条路径恰好经过每条边1次。

    如果可以构造,输出途径的点的编号。

    否则输出No Solution。

    其实想明白所谓的字母对只是无向边的话,这道题就是很清晰的欧拉路径了。

    ——以下来自欧拉回路路径求解 - STILLxjy - CSDN博客——

    Hierholzer 算法:
    另一种计算欧拉路的算法是 Hierholzer 算法。这种算法是基于这样的观察:
    这里写图片描述
    在手动寻找欧拉路的时候,我们从点 4 开始,一笔划到达了点 5,形成路径 4-5-2-3-6-5。此时我们把这条路径去掉,则剩下三条边,2-4-1-2 可以一笔画出。

    这两条路径在点 2 有交接处(其实点 4 也是一样的)。那么我们可以在一笔画出红色轨迹到达点 2 的时候,一笔画出黄色轨迹,再回到点 2,把剩下的红色轨迹画完。

    由于明显的出栈入栈过程,这个算法可以用 DFS 来描述。
    如果想看得更仔细一点,下面是从点 4 开始到点 5 结束的 DFS 过程,其中 + 代表入栈,- 代表出栈。
    4+ 5+ 2+ 3+ 6+ 5+ 5- 6- 3- 1+ 4+ 2+ 2- 4- 1- 2- 5- 4-
    我们把所有出栈的记录连接起来,得到
    5-6-3-2-4-1-2-5-4

    诸位看官可以自己再选一条路径尝试一下。不过需要注意的是,起始点的选择和 Fleury 要求的一样。
    这个算法明显要比 Fleury 高效,它不用判断每条边是否是一个桥。

    Hierholzer的复杂度是$O(E)$的,所以就套欧拉路径的板子就好啦。

    (实在没懂怎么“计算得到”n的规模,好在不用这个条件QAQ

     1 /*
     2     qwerta
     3     P1341 无序字母对
     4     Accepted
     5     100
     6     代码 C++,1.46KB
     7     提交时间 2018-09-30 11:11:47
     8     耗时/内存
     9     28ms, 1052KB
    10 */
    11 #include<algorithm>
    12 #include<iostream>
    13 #include<cstdio>
    14 #include<stack>
    15 using namespace std;
    16 int g[253][253];
    17 int d[253];//度数
    18 stack<int>st;//这个是记录栈,不是搜索栈!    
    19 void dfs(int x)//dfs找点
    20 {
    21     for(int j='A';j<='z';++j)//这样循环就可以保持字典序最小啦
    22     if(g[x][j])
    23     {
    24         g[x][j]--;
    25         g[j][x]--;//反向边也要删
    26         dfs(j);//继续递归
    27     }
    28     st.push(x);//出栈的时候记录下来
    29     return;
    30 }
    31 int fa[257];//用并查集维护是否有多个联通块
    32 int fifa(int x)
    33 {
    34     if(fa[x]==x)return x;
    35     return fa[x]=fifa(fa[x]);
    36 }
    37 int main()
    38 {
    39     //freopen("a.in","r",stdin);
    40     ios::sync_with_stdio(false);
    41     cin.tie(false),cout.tie(false);//关闭同步流(cin伴侣
    42     int n;
    43     cin>>n;
    44     for(int i='A';i<='z';++i)//初始化并查集
    45     fa[i]=i;
    46     for(int i=1;i<=n;++i)
    47     {
    48         char x,y;
    49         cin>>x>>y;
    50         g[x][y]++;
    51         g[y][x]++;//临接矩阵存边
    52         d[x]++;
    53         d[y]++;//度数++
    54         int u=fifa(x),v=fifa(y);
    55         if(u!=v)fa[u]=v;//维护并查集
    56     }
    57     //判定是否有解
    58     int num=0;
    59     for(int i='A';i<='z';++i)
    60     if(d[i]%2==1)num++;
    61     if(num!=0&&num!=2){cout<<"No Solution";return 0;}
    62     int tag=0;
    63     for(int i='A';i<='z';++i)
    64     if(d[i])
    65     {
    66         if(!tag)tag=i;
    67         else if(fifa(tag)!=fifa(i)){cout<<"No Solution";return 0;}
    68     }
    69     //找是否有奇点
    70     int s=-1;
    71     for(int i='A';i<='z';++i)
    72     if(d[i]%2==1){s=i;break;}
    73     if(s==-1)//如果没有奇点就找AscII最小的点
    74       for(int i='A';i<='z';++i)
    75       if(d[i]){s=i;break;}
    76     dfs(s);//递归找点
    77     while(!st.empty())
    78     {
    79         cout<<(char)st.top();
    80         st.pop();
    81     }//输出
    82     return 0;
    83 }

    皮一下:

  • 相关阅读:
    vim编辑器替换以及全局替换
    Linux下grep显示前后几行信息
    Pymol里常用到的命令,随用随记
    硬盘里有文件错误,导致删除不了文件,可以使用如下方法
    解决Host key verification failed
    tcl语言杂记
    python脚本后台运行的几种方式
    centos设置连续登录3次密码错误自动锁定账户3分钟
    ubuntu安装显卡驱动
    虚拟交换机(OVS)之结构印象
  • 原文地址:https://www.cnblogs.com/qwerta/p/9728665.html
Copyright © 2020-2023  润新知