• P1127 词链


    方法1:开始想的是先把所有字符串按照字典序升序排一下,然后从前向后以每一个单词为起点爆搜一下,第一个得到的答案就是字典序最小的答案,这个做法是对的,但是どうしても会被卡掉,因为复杂度比较高(由于剪枝不是很方便,所以直接废掉)。

    #include<iostream>
    #include<string>
    #include<vector>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;
    
    const int N = 1010;
    
    vector<string> node;
    vector<int> path;
    int n;
    int st[N];
    
    int dfs(int u, int cnt){
        st[u] = 1;
        
        if(cnt == n) return 1;
        
        for(int i = 0; i < n; i ++){
            if(st[i] == 0 && node[i][0] == node[u].back()){
                st[i] = 1;
                path.push_back(i);
                if(dfs(i, cnt + 1)) return 1;
                path.pop_back();
                st[i] = 0;
            }
        }
        
        return 0;
    }
    
    int main(){
        cin >> n;
        
        for(int i = 0; i < n; i ++){
            string s;
            cin >> s;
            
            node.push_back(s);
        }
        
        sort(node.begin(), node.end());
        
        for(int i = 0; i < n; i ++){
            path.push_back(i);
            int k = dfs(i, 1);
            
            if(k){
                for(int i = 0; i < path.size(); i ++){ 
                    cout << node[path[i]];
                    if(i + 1 < path.size()) cout << '.';
                }
                return 0;
            }
            memset(st, 0, sizeof st);
            path.pop_back();
        }
        
        cout << "***" << endl;
        
        return 0;
    }
    

    方法2:通过给的所有单词,建图(这个图不用实际地建出来,只需要用结点建立并查集(判断底图连通性)并记下每一个结点的入度和出度(判断欧拉图)即可),并判断是否为欧拉图,若是欧拉图则存在一条词链,否则不存在。

    建图方法:以每一个单词的首尾字母作为结点,单词作为边建图(此图最多26个结点),所以如果此图是欧拉图,那么必存在一条通路,将所有边访问且仅访问一次,那么就找到了一条词链,否则必定不存在词链。

    判断过程是:

    1. 由于欧拉图必定连通,所以可以初步判断一下底图(指和有向图长得一摸一样的无向图)是否连通(可用并查集判断),若不连通必不行。
    2. 欧拉图要么所有结点都满足入度 = 出度, 要么只存在一个入度 = 出度 + 1和一个出度 = 入度 + 1的结点,其他结点满足入度 = 出度,所以据此再推翻一批不存在词链的单词。

    判断过后,图必满足abs(入度 - 出度) = 1的结点数cnt = 0或2

    cnt = 0时,随便找一个结点出发都能一笔画,根据题目要求找以字典序最小的字母为首字母的单词出发dfs即可。

    cnt = 2时,找到以那个出度 - 入度 = 1的字母(唯一)为首字母的单词作为起始点,dfs一遍就一定可以找到一条词链。

    以上两种情况的dfs,由于之前已经按照单词字典序排序,所以找到的一定是字典序最小的词链。

    综上,其实是利用欧拉图的概念快速找到词链的起始字母,然后搜一遍即可,省去了其他不可能的情况。

    #include<iostream>
    #include<string>
    #include<vector>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;
    
    const int T = 1010, N = 26;
    
    vector<string> str;
    vector<int> path;
    int n;
    int st[T];
    int p[N], belong[N]; // belong[t]判断t是否在图中
    int in[N], out[N];
    
    int find(int x){
        if(x != p[x]) p[x] = find(p[x]);
        return p[x];
    }
    
    int dfs(int u, int cnt){
        if(cnt == n) return 1;
        
        for(int i = 0; i < n; i ++){
            if(str[i][0] - 'a' == u && st[i] == 0){
                path.push_back(i);
                st[i] = 1;
                if(dfs(str[i].back() - 'a', cnt + 1)) return 1;
                st[i] = 0;
                path.pop_back();
            }
        }
        
        return 0;
    }
    
    int main(){
        cin >> n;
        
        for(int i = 0; i < n; i ++){
            string s;
            cin >> s;
            
            str.push_back(s);
        }
        
        sort(str.begin(), str.end()); // 排序确保搜索字典序最小
        
        for(int i = 0; i < N; i ++) p[i] = i;
        
        for(int i = 0; i < n; i ++){
            int a = str[i][0] - 'a', b = str[i].back() - 'a';
            int pa = find(a), pb = find(b);
            if(pa != pb) p[pb] = pa;
            
            belong[a] = belong[b] = 1;
            in[b] ++, out[a] ++;
        }
        
        // 判断底图连通性
        int flag = 0;
        for(int i = 0; i < N; i ++)
            if(p[i] == i && belong[i]) flag ++;
        
        if(flag > 1){
            cout << "***";
            return 0;
        }
        
        // 判断欧拉图
        int cnt = 0, start = 0;
        
        for(int i = 0; i < N; i ++){
            int k = out[i] - in[i];
            
            if(abs(k) > 1){
                cout << "***";
                return 0;
            }
            
            if(abs(k) == 1){
                cnt ++;
                if(k == 1) start = i; // 记下起始字母
            }
        }
        
        
        if(cnt != 0 && cnt != 2){
            cout << "***";
            return 0;
        }
        
        if(cnt == 2) dfs(start, 0);
        if(cnt == 0)
            for(int i = 0; i < N; i ++)
                if(belong[i]){
                    dfs(i, 0);
                    break;
                }
        
        for(int i = 0; i < path.size(); i ++){
            cout << str[path[i]];
            if(i < path.size() - 1) cout << '.';
        }
        
        return 0;
    }
    
  • 相关阅读:
    JDK的安装及环境变量部署
    计算机常用运行指令
    Linux基础2
    Linux基础1
    Oracle数据库基础(2)
    Oracle数据库的基础(1)
    测试用例的设计
    软件测试基础
    转化课-环境变量
    转化课-计算机基础及上网过程
  • 原文地址:https://www.cnblogs.com/tomori/p/14317841.html
Copyright © 2020-2023  润新知