单词游戏 题解

四倍经验

51nod 2875 单词游戏

acwing 1185. 单词游戏

洛谷 SPOJ WORDS1 - Play on Words

单词 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;
}
posted @ 2024-09-11 09:29  sad_lin  阅读(6)  评论(0编辑  收藏  举报