题目链接
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.’
1.题目大意:输入n个单词,问是否能把所有单词首尾相连排成一个序列,满足序列中每个单词的首字母和上一个单词的尾字母相同。输入中有重复单词
2.这个题目写了好久,找到的博客写的都不咋滴,注释不多思路讲的也不太清晰。最难受的是看到一篇写的还行,研究了一个小时搞懂了,结果交不过,Ctrl+V的也WA,证明这是没通过的代码,真的好烦。万般无奈下,去洛谷碰碰运气,结果真的有!好好看了几篇,真的理解消化了,下面由我为君娓娓道来
3.首先应该知道什么是欧拉回路,在了解欧拉通路的基础上,再来看这道题。首先应该分析这是无向图还是有向图,单看两个单词的连接仅有一条边,我们觉得这是无向图。但是并不能按无向图的欧拉通路来判断,因为单词的序列是有头有尾的(欧拉回路先不考虑),那么这时我们将图按无向图处理,然后判断欧拉通路时按有向图的方法去判断
4.接下来是图如何去存。第一步肯定是把字母转换为0-25的数字。接着我们看样例,显然每个单词的首尾字母是我们判断的依据,由于首尾字母中间可以看做一条必定连接的边,那么我们可以将首尾字母看做一个"节点"。那么对于无向图中的一个"节点",首字母和尾字母都可以将一方作为入度,另一方作为出度,这对答案无影响(亲测)
5.最后就是如何判断图的连通性了,下面有两种方法,第一种是dfs,但是用邻接矩阵存图时要注意将首尾连接的边看做两条有向边保存,有重复并不影响。然后应该注意由于是无向图那么dfs传入任何一个节点都能判断连通性。第二种方法是并查集,判断是不是输入的节点属于一个集合即可
dfs代码:(10ms)
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
const int maxn=30;
int G[maxn][maxn],in[maxn],out[maxn];
bool vis[maxn]; //判断哪些字母输入了并用于连通性的判断
string s;
int n,t;
int start; //传入的节点
void dfs(int u){
vis[u]=0;
for(int i=0;i<26;i++)
if(G[u][i] && vis[i]) dfs(i);
}
bool solve(){
int num1=0,num2=0; //记录起点和终点的个数
dfs(start);
for(int i=0;i<=26;i++){
if(vis[i]) return false; //上面判断过连通性,如仍有节点没有连通则失败
if(in[i]!=out[i]){
if(in[i]==out[i]+1)
num1++;
else if(out[i]==in[i]+1)
num2++;
else return false; //出现其他情况就直接退出
}
}
if( (num1==1 && num2== 1) || (num1==0 && num2==0)) return true;
else return false;
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
memset(vis,0,sizeof vis);
memset(G,0,sizeof G);
memset(in,0,sizeof in);
memset(out,0,sizeof out);
while(n--){
cin>>s;
int l=s[0]-'a';
int r=s[s.size()-1]-'a';
G[l][r]++;G[r][l]++; //注意无向图
in[l]++;out[r]++; //in[r]++,out[l]++;也行的
vis[l]=vis[r]=1;
start=l;
}
if(solve()) printf("Ordering is possible.\n");
else printf("The door cannot be opened.\n");
}
return 0;
}
并查集代码:(20ms)
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
const int maxn=30;
int father[maxn],in[maxn],out[maxn];
bool vis[maxn];
string s;
int n,t;
int Find(int x){
return father[x]==x?x:father[x]=Find(father[x]);
}
void Union(int x,int y){
int fx=Find(x);
int fy=Find(y);
if(fx!=fy) father[fx]=fy;
}
bool solve(){
int num1=0,num2=0;
int flag=0;
for(int i=0;i<26;i++) if(father[i]==i && vis[i]) flag++;
if(flag!=1) return false;
for(int i=0;i<=26;i++){
if(in[i]!=out[i]){
if(in[i]==out[i]+1)
num1++;
else if(out[i]==in[i]+1)
num2++;
else return false;
}
}
if( (num1==1 && num2== 1) || (num1==0 && num2==0)) return true;
else return false;
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
memset(vis,0,sizeof vis);
memset(in,0,sizeof in);
memset(out,0,sizeof out);
for(int i=0;i<26;i++) father[i]=i;
while(n--){
cin>>s;
int l=s[0]-'a';
int r=s[s.size()-1]-'a';
Union(l,r);
in[l]++;out[r]++;
vis[l]=vis[r]=1;
}
if(solve()) printf("Ordering is possible.\n");
else printf("The door cannot be opened.\n");
}
return 0;
}