【ACWing】1185. 单词游戏

题目地址:

https://www.acwing.com/problem/content/1187/

N N N个盘子,每个盘子上写着一个仅由小写字母组成的英文单词。你需要给这些盘子安排一个合适的顺序,使得相邻两个盘子中,前一个盘子上单词的末字母等于后一个盘子上单词的首字母。请你编写一个程序,判断是否能达到这一要求。

输入格式:
第一行包含整数 T T T,表示共有 T T T组测试数据。每组数据第一行包含整数 N N N,表示盘子数量。接下来 N N N行,每行包含一个小写字母字符串,表示一个盘子上的单词。一个单词可能出现多次。

输出格式:
如果存在合法解,则输出”Ordering is possible.”,否则输出”The door cannot be opened.”

数据范围:
1 ≤ N ≤ 1 0 5 1≤N≤10^5 1N105
单词长度均不超过 1000 1000 1000

如果把每个字母看成是图上的一个点,盘子看成是边,那么题目相当于在问一个有向图是否存在欧拉路径。一个有向图存在欧拉路径的充要条件是图是连通的(本题不会存在孤立点,所以考虑点连通即可),并且要么所有点的入度都等于出度(则存在欧拉回路),要么存在两个特殊的点,除了这两个点之外所有点入度都等于出度,而这两个点一个是起点一个是终点,起点是出度比入度多 1 1 1,终点是入度比出度多 1 1 1(则存在非回路的欧拉路径)。连通性可以用并查集判断。代码如下:

#include <iostream>
#include <cstring>
using namespace std;

const int N = 30;
int n;
// p数组是并查集里的parent数组
int din[N], dout[N], p[N];
// st[i]记录字母i是否出现过
bool st[N];

// 返回并查集里的i所在树的树根
int find(int x) {
    
    
    if (x != p[x]) p[x] = find(p[x]);
    return p[x];
}

void merge(int x, int y) {
    
    
    int px = find(x), py = find(y);
    if (px == py) return;
    p[px] = py;
}

int main() {
    
    
    string s;

    int T;
    cin >> T;
    while (T--) {
    
    
        // 重置一下各个变量
        memset(din, 0, sizeof din);
        memset(dout, 0, sizeof dout);
        memset(st, 0, sizeof st);
        for (int i = 0; i < 26; i++) p[i] = i;

        cin >> n;
        for (int i = 0; i < n; i++) {
    
    
            cin >> s;
            // 说明从a到b有条边,则a的出度加1,b的入度加1
            int a = s[0] - 'a', b = s[s.size() - 1] - 'a';
            st[a] = st[b] = true;
            dout[a]++, din[b]++;
			
			// 做合并操作
            merge(a, b);
        }

        int start = 0, end = 0;
		
		// res存是否存在欧拉路径
        bool res = true;
        // 看一下能充当起点和终点的顶点有多少个,并且如果某个点的入度与出度个数差大于了1,则肯定不存在欧拉路
        for (int i = 0; i < 26; i++) {
    
    
            if (din[i] != dout[i]) {
    
    
                if (din[i] == dout[i] + 1) end++;
                else if (din[i] + 1 == dout[i]) start++;
                else {
    
    
                    res = false;
                    break;
                }
            }
        }    
		
		// 如果每个顶点的度的情况都是合法的,但是起点和终点的情况非法,那么也不存在欧拉路径
        if (res && !(start == 0 && end == 0 || start == 1 && end == 1)) res = false;
		
		// 看一下是否所有顶点都是连通的
        int parent = -1;
        for (int i = 0; i < 26; i++)
            if (st[i]) {
    
    
                if (parent == -1) parent = find(i);
                else if (parent != find(i)) {
    
    
                    res = false;
                    break;
                }
            }
        
        cout << (res ? "Ordering is possible." : "The door cannot be opened.") << endl;
    }

    return 0;
}

时空复杂度 O ( N ) O(N) O(N)

猜你喜欢

转载自blog.csdn.net/qq_46105170/article/details/115107720