SPOJ Play on Words

传送门

WORDS1 - 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, equal to about 500) 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 Nlines 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.".

Example

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.
-----------------------------------------------------
题目要求将所有单词排列成such that the first letter of each word is equal to the last letter of the previous word。这道题是典型的欧拉路径问题,这种问题分析起来还是有一定难度的。
我们考虑如何建图
将26个字母看成节点,将每个单词看成从其首字母向尾字母的有向边。
问题转换成判断有向图中是否存在一条欧拉路径
-----------------------------------------------------
Solution
一个有向图存在欧拉路径的必要条件是:
每个节点的出度都等于入度

存在一个节点s,其入度比出度少1,存在一个节点t,其出度比入度多1
在前一种情况下若图中存在欧拉路径,那么起点和终点必然是同一点,而且任意出度不为0的节点都可作为起点,
在后一种情况下若图中存在欧拉路径,那s必然是起点,t必然是终点。
-----------------------------------------------------
但上述条件仅仅是必要条件,除此之外还要求图“连通”,
即要求从前面确定的起点出发可以走完所有边(这好像是废话)
其实前面的必要条件仅能快速判断出欧拉路径不存在的情况,
对于欧拉路径存在的情况仍然要通过遍历图中的边来确认。
------------------------------------------------------
下面考虑如何遍历有向图(可能存在重边、自环)中的边
如果用vector<int> g[N存图的话不是很方便,而链式前向星是比较方便的
请特别注意下面代码中加粗的三行,并请考虑如果将那三行改成下面两种写法
有什么问题
void dfs(int u){
    for(; ~head[u]; head[u]=E[head[u]].nt){
        int &v=E[head[u]].to;
        dfs(v);
    }
}

void dfs(int u){
    for(; ~head[u];){
        int &v=E[head[u]].to;
        head[u]=E[head[u]].nt;
        dfs(v);
    }
}

----------------------------------------------------------
Code
#include <bits/stdc++.h>
using namespace std;
char s[1005];
int in[26], out[26];
const int M(1e5+5);
struct edge{
    int to, nt;
}E[M];
int head[26];
void dfs(int u){
    for(; ~head[u];){
        int v=E[head[u]].to;    //this edge has been used
        head[u]=E[head[u]].nt;
        dfs(v);
    }
}
bool solve(int n){
    bool flag=true;
    for(int i=0; i<26; i++)if(in[i]!=out[i]){flag=false; break;}
    if(flag){
        for(int i=0; i<26; i++) if(out[i]){dfs(i); break;}
        for(int i=0; i<26; i++) if(~head[i]) return 0;
        return 1;
    }
    int s=-1, t=-1;
    for(int i=0; i<26; i++){
        if(in[i]!=out[i]){
            if(abs(in[i]-out[i])!=1) return 0;
            if(in[i]>out[i]){
                if(~t) return 0;
                t=i;
            }
            else if(in[i]<out[i]){
                if(~s) return 0;
                s=i;
            } 
        }
    }
    if(~s&&~t){
        dfs(s); 
        for(int i=0; i<26; i++) if(~head[i]) return 0;
        return 1;
    }
    return 0;
}
int main(){
    int T; scanf("%d", &T);
    for(int n; T--;){
        scanf("%d", &n);
        memset(in, 0, sizeof(in));
        memset(out, 0, sizeof(out));
        memset(head, -1, sizeof(head));
        for(int len, u, v, id=0, _=n; _--;){
            scanf("%s", s);
            len=strlen(s);
            u=s[0]-'a', v=s[len-1]-'a';
            out[u]++, in[v]++;
            E[id]={v, head[u]}, head[u]=id++;
        }
        puts(solve(n)?"Ordering is possible.":"The door cannot be opened.");
    }
}