SPOJ Play on Words

传送门

WORDS1 - Play on Words

#graph-theory #euler-circuit

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.");
    }
}
				
时间: 2024-10-12 08:52:46

SPOJ Play on Words的相关文章

SPOJ 705 Distinct Substrings(后缀数组)

[题目链接] http://www.spoj.com/problems/SUBST1/ [题目大意] 给出一个串,求出不相同的子串的个数. [题解] 对原串做一遍后缀数组,按照后缀的名次进行遍历, 每个后缀对答案的贡献为n-sa[i]+1-h[i], 因为排名相邻的后缀一定是公共前缀最长的, 那么就可以有效地通过LCP去除重复计算的子串. [代码] #include <cstdio> #include <cstring> #include <algorithm> usi

SPOJ 3273

传送门: 这是一道treap的模板题,不要问我为什么一直在写模板题 依旧只放代码 1 //SPOJ 3273 2 //by Cydiater 3 //2016.8.31 4 #include <iostream> 5 #include <cstring> 6 #include <ctime> 7 #include <cmath> 8 #include <cstdlib> 9 #include <string> 10 #include

SPOJ CRAN02 - Roommate Agreement

题目链接:http://www.spoj.com/problems/CRAN02/ 题目大意:N个数字组成的序列,和为0的连续子序列的个数.N<1e6 解题思路:计算前缀和,统计每个数字出现的次数,那么对于数字sum[i], 如果存在k个sum[i],则代表有C(k, 2)个序列和为0,而如果sum[i] = 0,则还要累加上对应的k值. 代码: 1 ll n; 2 int a[maxn]; 3 ll sum[maxn]; 4 map<int, int> mmp; 5 6 void so

spoj GCJ1C09C Bribe the Prisoners

题目链接: http://www.spoj.com/problems/GCJ1C09C/ 题意: In a kingdom there are prison cells (numbered 1 to P) built to form a straight line segment. Cells number i and i+1 are adjacent, and prisoners in adjacent cells are called "neighbours." A wall wi

SPOJ QTREE Query on a tree ——树链剖分 线段树

[题目分析] 垃圾vjudge又挂了. 树链剖分裸题. 垃圾spoj,交了好几次,基本没改动却过了. [代码](自带常数,是别人的2倍左右) #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define maxn 20005 int T,n,fr[maxn],h[maxn],to[maxn],ne[maxn]

BZOJ 2588: Spoj 10628. Count on a tree 主席树+lca

2588: Spoj 10628. Count on a tree Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. Input 第一行两个整数N,M. 第二行有N个整数,其中第i个整数表示点i的权值. 后面N-1行每行两个整数(x,y),表示点x到点y有一条边. 最后M行每行两个整数(u,v,k),表示一组询问.

BZOJ 1002 + SPOJ 104 基尔霍夫矩阵 + 一个递推式。

BZOJ 1002 高精度 + 递推 f[1] = 1; f[2] = 5; f[i] = f[i - 1] * 3 - f[i - 2] + 2; SPOJ 104 裸 + 不用Mod 1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <algorithm> 5 #include <iostream> 6 7 using namespace std;

SPOJ QTREE 系列解题报告

题目一 : SPOJ 375 Query On a Tree http://www.spoj.com/problems/QTREE/ 给一个树,求a,b路径上最大边权,或者修改a,b边权为t. 1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <cstdlib> 5 #include <algorithm> 6 7 using namespace s

BZOJ 2226: [Spoj 5971] LCMSum( 数论 )

∑lcm(i,n) = ∑ i*n/(i,n) = ∑d|n∑(x,n)=d x*n/d = ∑d|n∑(t,n/d)=1t*n = n∑d|nf(d). f(d)表示1~d中与d互质的数的和, 即f(d) = d*φ(d)/2(d>=2). 然后O(n)筛φ, 每次询问暴力算即可...最大是100w,sqrt(100w)=1000内的质数是168个, 所以复杂度是O(n + T*168), 可以AC  ----------------------------------------------

bzoj 2482: [Spoj GSS2] Can you answer these queries II 线段树

2482: [Spoj1557] Can you answer these queries II Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 145  Solved: 76[Submit][Status][Discuss] Description 给定n个元素的序列. 给出m个询问:求l[i]~r[i]的最大子段和(可选空子段). 这个最大子段和有点特殊:一个数字在一段中出现了两次只算一次. 比如:1,2,3,2,2,2出现了3次,但只算一次,