单词游戏 题解
四倍经验
洛谷 SPOJ WORDS1 - Play on Words
题意
将 \(n\) 个单词,问能否把这些单词首尾相接的连起来(头尾字母要一样)。
分析
我们可以将每一个字母看成一个节点,这样我们就有了一个包含 26 个节点的图,对于读入的单词,我们将首字母和尾字母对应的节点之间建有向边(中间的字母没什么用就不管了)。
此时我们发现题目简化为:在一个有向图上,找到一条路径,使每条边都经过一次并且仅经过一次。
可以发现这正是欧拉路径问题,但是!你并不需要真的建图来跑一遍欧拉路径,只需对这张图判断是否存在欧拉路径即可。
欧拉路径的判定(有向图):除了起点和终点,每个点的入度等于出度。起点入度比出度小 1,终点入度比出度大 1。
注意判断图是否连通,用并查集就可以。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n;
int t;
int fa[100005];//为了并查集
int in[100005];//入度
int out[100005];//出度
int find(int x){//用并查集维护连通性
return (fa[x]==x)?x:find(fa[x]);
}
int main() {
ios::sync_with_stdio(false);
cin>>t;
while(t--){
cin>>n;
for(int i=0;i<26;i++){//初始化
in[i]=0;
out[i]=0;
fa[i]=i;
}
for(int i=1;i<=n;i++){
string s;
cin>>s;
int l=s[0]-'a';//首字母
int r=s[s.size()-1]-'a';//尾字母
fa[find(l)]=find(r);//连有向边
in[r]++;//入度增加
out[l]++;//出度增加
}
int cnt=0;
for(int i=0;i<26;i++){//判断连通性,判断是否存在不合法的节点
if(((out[i]||in[i])&&(find(i)==i))||abs(in[i]-out[i])>1){
cnt++;
}
}
if(cnt>1){//不合法
cout<<"The door cannot be opened.\n";
continue;
}
int s=0;//起点数
int t=0;//终点数
for(int i=0;i<26;i++){
if(in[i]>out[i]){//终点数统计
t++;
}
if(in[i]<out[i]){//起点数统计
s++;
}
}
if((s!=t)||t>1){//起点终点数都应该为 1
cout<<"The door cannot be opened.\n" ;
continue;
}
cout<<"Ordering is possible.\n";
}
return 0;
}