10129 - Play on Words

Play on Words

Some of the secret doors contain a very interesting word puzzle. The team of archaeologists has to solve it to open that doors. Because there is no other way to open the doors, the puzzle is very important for us.

There is a large number of magnetic plates on every door. Every plate has one word written on it. The plates must be arranged into a sequence in such a way that every word begins with the same letter as the previous word ends. For example, the word ‘acm’ can be followed by the word ‘motorola’.Your task is to write a computer program that will read the list of words and determine whether it is possible to arrange all of the plates in a sequence (according to the given rule) and consequently to open the door.

Input

The input consists of T test cases. The number of them (T) is given on the first line of the input file. Each test case begins with a line containing a single integer number N that indicates the number of plates (1≤N≤100000). Then exactly N lines follow, each containing a single word. Each word contains at least two and at most 1000 lowercase characters, that means only letters ‘a’ through ‘z’ will appear in the word. The same word may appear several times in the list.

Output

Your program has to determine whether it is possible to arrange all the plates in a sequence such that the first letter of each word is equal to the last letter of the previous word. All the plates from the list must be used, each exactly once. The words mentioned several times must be used that number of times.

If there exists such an ordering of plates, your program should print the sentence ‘Ordering is possible.’. Otherwise, output the sentence ‘The door cannot be opened.’

Sample Input

3
2
acm
ibm
3
acm
malform
mouse
2
ok
ok

Sample Output

The door cannot be opened.
Ordering is possible.
The door cannot be opened.

输入n(n≤100000)个单词,是否可以把所有这些单词排成一个序列,使得每个单词的 第一个字母和上一个单词的最后一个字母相同(例如acm、malform、mouse)。每个单词最 多包含1000个小写字母。输入中可以有重复单词。

在欧拉道路中,”进”和”出”是对应的——除了起点和终点外,其他点的”进 出”次数应该相等。换句话说,除了起点和终点外,其他点的度数(degree)应该是偶数。很可惜,在七桥问题中,所有4个点的度数均是奇数(这样的点也称奇点),因此不可能存在欧拉道路。上述条件也是充分条件——如果一个无向图是连通的,且最多只有两个奇点,则一 定存在欧拉道路。如果有两个奇点,则必须从其中一个奇点出发,另一个奇点终止;如果奇点不存在,则可以从任意点出发,最终一定会回到该点(称为欧拉回路)。

用类似的推理方式可以得到有向图的结论:最多只能有两个点的入度不等于出度,而且必须是其中一个点的出度恰好比入度大1(把它作为起点),另一个的入度比出度大1(把它作为终点)。当然,还有一个前提条件:在忽略边的方向后,图必须是连通的。

//#define LOCAL
#include <iostream>
#include <string>
#include <vector>
#include <cstring>

using namespace std;

const int maxNum = 256;

// 并查集
int parent[maxNum];
// 树高度
int ranks[maxNum];

// 初始化并查集
void initSet() {
    for(int ch = 'a'; ch <= 'z'; ch++) {
        parent[ch] = ch;
        ranks[ch] = 0;
    }
}

// 递归追溯根节点
// 并将由该点到根节点上所有节点的parent置为根节点
int findSet(int x) {
    if(parent[x] == x) {
        return x;
    } else {
        return parent[x] = findSet(parent[x]);
    }
}

// x,y是否同一个并查集
bool sameSet(int x, int y) {
    return findSet(x) == findSet(y);
}

// 合并x,y所在的并查集
void uniteSet(int x, int y) {
    x = findSet(x);
    y = findSet(y);
    // 同一并查集
    if(x == y) {
        return;
    }
    // x所在并查集的高度小于y的
    if(ranks[x] < ranks[y]) {
        parent[x] = y;
    } else {
        parent[y] = x;
        // 高度相等,x高度+1
        if(ranks[x] == ranks[y]) {
            ranks[x]++;
        }
    }

}

// 是否出现
int occur[maxNum];
// 出度数组
int deg[maxNum];


int main() {
    #ifdef LOCAL
        freopen("data10129.in", "r", stdin);
        freopen("data10129.out", "w", stdout);
    #endif // LOCAL

    int T;
    cin >> T;
    while(T--) {
        int n;
        string word;
        cin >> n;
        // 初始化
        memset(occur, 0, sizeof(occur));
        memset(deg, 0, sizeof(deg));
        initSet();

        // 连通块个数
        int cc = 26;

        for(int i = 0; i < n; i++) {
            cin >> word;
            char c1 = word[0];
            char c2 = word[word.length() - 1];
            // c1出度+1
            deg[c1]++;
            // c2出度-1
            deg[c2]--;
            // c1,c2都出现过了
            occur[c1] = occur[c2] = 1;
            // c1,c2不是一个并查集
            if(!sameSet(c1, c2)) {
                // 合并c1,c2所在的并查集
                uniteSet(c1, c2);
                // 连通块个数-1
                cc--;
            }
        }

        // 存放所有出度不等于入度的顶点
        vector<int> d;
        for(int ch = 'a'; ch <= 'z'; ch++) {
            // 没出现过的字母
            if(!occur[ch]) {
                cc--;
            } else if(deg[ch] != 0) {
                d.push_back(deg[ch]);
            }
        }

        bool flag = false;
        // 连通分支为1且
        // 容器为空或者容器有两个元素,一个为起始点,一个为终止点
        // 起始点出度=入度+1,终止点入度=出度+1
        if(cc == 1 && (d.empty() || (d.size() == 2 && (d[0] == 1 || d[0] == -1)))) {
            flag = true;
        }
        if(flag) {
            cout << "Ordering is possible." << endl;
        } else {
            cout << "The door cannot be opened." << endl;
        }
    }
    return 0;
}
#include <iostream>
#include <cstring>
#include <string>
#include <stdio.h>
//#define LOCAL
using namespace std;
/** 该种解法通过了UVa自带的Debug平台的测试 但是提交还是WA,也就说肯定还有一些 边界问题没有注意,希望以后有时间,再回 来看看能不能找到问题所在 **/
const int maxNum = 26;

string s;
// 图
int words[maxNum][maxNum];
// 底图,也就是有向图忽略边方向后得到的无向图
int wordsTmp[maxNum][maxNum];
// 边访问数组
int visited[maxNum][maxNum];

void euler(int u) {
    for(int v = 0; v < maxNum; v++) {
        if(wordsTmp[u][v] && !visited[u][v]) {
            visited[u][v] = visited[v][u] = 1;
            euler(v);
        }
    }
}


int main() {
    #ifdef LOCAL
        freopen("data10129.in", "r", stdin);
        freopen("data10129.out", "w", stdout);
    #endif // LOCAL
    // T组用例
    int T;
    cin >> T;
    while(T--) {
        int N;
        // N条单词
        cin >> N;
        memset(words, 0, sizeof(words));
        memset(wordsTmp, 0, sizeof(wordsTmp));

        int bn, ed;
        for(int i = 0; i < N; i++) {
            cin >> s;
            // cout << s[0] - 'a' << " " << s[s.length() - 1] - 'a' << endl;
            bn = s[0] - 'a';
            ed = s[s.length() - 1] - 'a';
            words[bn][ed]++;
            wordsTmp[bn][ed]++;
            wordsTmp[ed][bn]++;
        }
        int numIn = 0;
        int numOut = 0;
        // 除起始点和终止点外其余顶点的入度必须等于出度
        for(int i = 0; i < maxNum; i++) {
            int sumRow = 0;
            int sumCol = 0;
            for(int j = 0; j < maxNum; j++) {
                if(i != j) {
                    // 出度
                    sumRow += words[i][j];
                    // 入度
                    sumCol += words[j][i];
                }
            }
            // 如果不是环路,则必有
            // 起始点出度比入度多一
            // 终止点入度比出度多一
            // 一般情况是等于,不做处理
            // 其他情况都表示该图不能构成欧拉道路
            if(sumRow == sumCol + 1) {
                numOut++;
            } else if(sumCol == sumRow + 1) {
                numIn++;
            } else if(sumCol == sumRow) {

            } else {
                numOut = numIn = maxNum;
            }
        }
        if(numOut > 1 || numIn > 1 || numOut != numIn) {
            cout << "The door cannot be opened." << endl;
            continue;
        }


        memset(visited, 0, sizeof(visited));
        euler(bn);

        bool flag = false;
        for(int i = 0; i < maxNum; i++) {
            for(int j = 0; j < maxNum; j++) {
                if(wordsTmp[i][j] && !visited[i]) {
                    flag = true;
                }
            }
        }
        if(flag) {
            cout << "The door cannot be opened." << endl;
        } else {
            cout << "Ordering is possible." << endl;
        }
    }
    return 0;
}

你可能感兴趣的:(ACM,uva,UVa10129)