• [luogu p1127] 词链


    传送门

    词链

    题目描述

    如果单词 (X) 的末字母与单词 (Y) 的首字母相同,则 (X)(Y) 可以相连成 (X.Y)。(注意:(X)(Y) 之间是英文的句号 .)。例如,单词 dog 与单词 gopher,则 doggopher 可以相连成 dog.gopher

    另外还有一些例子:

    • dog.gopher
    • gopher.rat
    • rat.tiger
    • aloha.aloha
    • arachnid.dog

    连接成的词可以与其他单词相连,组成更长的词链,例如:

    aloha.arachnid.dog.gopher.rat.tiger

    注意到,. 两边的字母一定是相同的。

    现在给你一些单词,请你找到字典序最小的词链,使得这些单词在词链中出现且仅出现一次。

    输入输出格式

    输入格式

    第一行是一个正整数 (n)(1 le n le 1000)),代表单词数量。

    接下来共有 (n) 行,每行是一个由 (1)(20) 个小写字母组成的单词。

    输出格式

    只有一行,表示组成字典序最小的词链,若不存在则只输出三个星号 ***

    输入输出样例

    输入样例 #1

    6
    aloha
    arachnid
    dog
    gopher
    rat
    tiger
    

    输出样例 #1

    aloha.arachnid.dog.gopher.rat.tiger
    

    说明

    • 对于 (40\%) 的数据,有 (n leq 10)
    • 对于 (100\%) 的数据,有 (n leq 1000)

    分析

    此题是一道挺复杂的搜索,推荐一做,挺考验码力。

    首先,我们可以把字母当成点,单词当成边。

    比如,word,就可以代表 (w ightarrow d) 这条边。然后dfs寻找最小的词链。但是啊同志,(n le 1000),这个复杂度裸爆搜,时间复杂度很危险。

    所以我们需要经过一个优化:欧拉路找起点优化

    首先,原题中要求词链中的词出现并且仅出现一次,说明题目是让我们找一条欧拉路径。

    对于非欧拉回路的欧拉路径,一定满足一个点的出度比入度大1,一个点的入度比出度大1,其余点入度与出度均相等

    而对于欧拉回路,一定满足所有点的入度与出度均相等

    因此,在找起点的时候,我们可以遍历所有点,如果发现一个出度比入度大1的点,就可以设置其为源点 (S),如果发现一个入度比出度大1的点,那么它就是终点 (E)

    • 如果在遍历过程中发现两个 (S),无满足的词链;
    • 如果在遍历过程中发现两个 (E),无满足的词链;
    • 如果在遍历过程中,发现一个点的入度和出度的关系既不是差1又不是相等,无满足的词链;
    • 如果遍历完成后,(S)(E),无满足的词链;
    • 如果遍历完成后,(E)(S),无满足的词链;
    • 以上判断结束后,如果真的没有 (S),说明这个图为欧拉回路,找一个字典序最小的单词开始dfs即可。

    你看,直接判断掉这么多不满足的图,是不是感觉倍赚呀~所以欧拉回路和路径的性质有时真的很有用。

    但是这样,就能直接开始dfs了吗?

    那,如果这个图不联通呢

    哈哈哈没错,还有这种情况,那这个怎么解决呢?

    并查集

    我们将所有在词首和词末出现的字母都新建一个代表字母的节点,并且如果有边那么就将他们合并,最后检查集合数是否为 (1) 即可。如果为 (1),说明联通;如果不是,那就没解了。

    好了,经过这么多筛选了,没有任何无解情况了,终于可以开始欢乐的dfs了!

    dfs前要把所有字符串按照字典序排列好哦。

    dfs的话。。实在是没啥好讲的了。直接套模板就行了。

    那就上代码吧!

    代码

    /*
     * @Author: crab-in-the-northeast 
     * @Date: 2020-09-26 13:17:28 
     * @Last Modified by: crab-in-the-northeast
     * @Last Modified time: 2020-09-26 20:06:22
     */
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <algorithm>
    #include <vector>
    
    const int maxn = 1005;
    const int maxl = 30;
    
    struct node {
        int v, id;
        std :: string word;
    };
    
    std :: vector <node> G[maxl];
    int ind[maxl], outd[maxl];
    bool letter[maxl], vis[maxn];
    
    int n;
    std :: string str[maxn], ans[maxn];
    
    int cti(char c) {
        return c - 'a' + 1;
    }
    
    void GG() {
        puts("***");
        exit(0);
    }
    
    struct ufs {
        int fa[10086];
        bool valid[10086];
        int siz;
    
        void init(int n) {
            siz = n;
            for (int i = 1; i <= n; ++i)
                fa[i] = i;
        }
    
        int find(int x) {
            while (x != fa[x]) x = fa[x] = fa[fa[x]];
            return x;
        }
        
        void unite(int x, int y) {
            fa[find(x)] = find(y);
        }
    
        bool judge(int x, int y) {
            return find(x) == find(y);
        }
    
        int get_setnum() {
            int setnum = 0;
            for (int i = 1; i <= siz; ++i)
                if (fa[i] == i && valid[i])
                    ++setnum;
            return setnum;
        }
    };
    
    void dfs(int u, int step) {
        if (step == n) {
            std :: printf("%s", ans[1].c_str());
            for (int i = 2; i <= n; ++i)
                std :: printf(".%s", ans[i].c_str());
            exit(0);
        }
    
        for (int i = 0; i < G[u].size(); ++i) {
            node e = G[u][i];
            if (!vis[e.id]) {
                vis[e.id] = true;
                ans[step + 1] = e.word;
                dfs(e.v, step + 1);
                vis[e.id] = false;
            }
        }
    
        return ;
    }
    
    int main() {
        std :: scanf("%d", &n);
        for (int i = 1; i <= n; ++i)
            std :: cin >> str[i];
        std :: sort(str + 1, str + 1 + n);
    
        ufs Gu;
        Gu.init(maxl);
    
        for (int i = 1; i <= n; ++i) {
            int first = cti(str[i][0]);
            int last = cti(str[i][str[i].length() - 1]);
            ++ind[last];
            ++outd[first];
            
            Gu.valid[last] = true;
            Gu.valid[first] = true;
            if (first != last) {
                if (!Gu.judge(first, last))
                    Gu.unite(first, last);
            }
    
            G[first].push_back((node){last, i, str[i]});
        }
    
        if (Gu.get_setnum() != 1)
            GG();
    
        for (int i = 1; i <= 26; ++i)
            letter[i] = Gu.valid[i];
    
        //puts("Ok");
    
        int S = 0, E = 0;
        for (int i = 1; i <= 26; ++i) {
            if (!letter[i]) continue;
            if (outd[i] == ind[i] + 1) {
                if (S)
                    GG();
                S = i;
            } else if (ind[i] == outd[i] + 1) {
                if (E)
                    GG();
                E = i;
            } else if (ind[i] == outd[i]) {
                continue;
            } else {
                GG();
            }
        }
    
        if ((S && !E) || (!S && E))
            GG();
        
        if (!S) S = cti(str[1][0]);
        
        dfs(S, 0);
        
        return 0;
    }
    

    评测记录

    评测记录

  • 相关阅读:
    oracle 12C linux centos7.5 安装 12C
    FizzBuzz
    批量判断能否telnet登录
    统计所有机器的挂载情况
    ffmpeg windows vs library 下载地址
    需求文档测试
    接口测试分析
    chrome网页截图
    不要为了测试写一个新系统
    C# 判断是否为数字
  • 原文地址:https://www.cnblogs.com/crab-in-the-northeast/p/luogu-p1127.html
Copyright © 2020-2023  润新知