解题思路:把单词的首尾字母想象成节点,单词本体想象成边,其实这就是一道欧拉道路的判别题目,判断是否存在”一笔画可以通过所有的边“。
针对于欧拉道路,有两种情况,一种是无向图,一种是有向图。
无向图:整张图是连通的,且最多有两个奇点,则一定存在欧拉道路。如果奇点数为0,则从任意点出发都可以,且最终一定返回该点。存在两个奇点,则其中一个为起点,另一个为终点。
有向图:在无向图情况下连通,最多只有两个点的入度不等于出度,且其中一点一定是入度比出度大1,另一个一定是入度比出度小1.
题目大意:
判断是否可以把单词排成一个序列,使得每个单词的第一个字母和上一个单词的最后一个字母相同。例如, motorola的后面可以接上acm。
你的任务是写一个程序, 读入一系列单词,然后计算确定它们是否有可能被排成这样的队列。
样例输入:
3 2 acm ibm 3 acm malform mouse 2 ok ok
样例输出:
The door cannot be opened. Ordering is possible. The door cannot be opened.
#include<cstdio>
#include<iostream>
#include<string>
#include<string.h>
#include<set>
using namespace std;
int root[100005]; //记录元素所在集合根部
int indegree[100005]; //记录入度
int outdegree[100005]; //记录出度
set<char> Q; //利用集合不重复的性质存放所有结点
int findroot(int x) //查找根部并把被查找元素直接指向根部
{
if(root[x]==-1) return x;
int tmp=findroot(root[x]);
root[x]=tmp;
return tmp;
}
int main()
{
int T;
cin>>T;
while(T--)
{
int n;
cin>>n;
Q.clear(); //集合的内容一定要清空!
memset(indegree,0,sizeof(indegree));
memset(outdegree,0,sizeof(indegree));
memset(root,-1,sizeof(root));
for(int i=0;i<n;i++)
{
string s;
cin>>s;
int a=findroot(s[0]-'a'); //这一段的并查集处理一定要注意!
int b=findroot(s[s.length()-1]-'a'); //
if(a!=b) //
{ //
root[a]=b; //
} //
Q.insert(s[0]);
Q.insert(s[s.length()-1]);
indegree[s[s.length()-1]-'a']++;
outdegree[s[0]-'a']++;
}
int jihe=0,jishu=0;
int st=0,end=0; //记录起点和终点数目
for(set<char>:: iterator it=Q.begin();it!=Q.end();it++) //这一段对于set元素的遍历非常重要!
{
if(root[*it-'a']==-1)
{
jihe++;
}
//if(indegree[*it-'a']-outdegree[*it-'a']==1||outdegree[*it-'a']-indegree[*it-'a']==1)
if(indegree[*it-'a']!=outdegree[*it-'a'])
{
jishu++;
if(indegree[*it-'a']-outdegree[*it-'a']==1)
end++;
if(indegree[*it-'a']-outdegree[*it-'a']==-1)
st++;
}
}
//cout<<jihe<<" "<<jishu<<endl;
if(jihe==1&&(jishu==0||jishu==2&&st&&end)) //集合数只能为1,记述点若为2则入度-出度=1和出度-入度=1点各一个
{
cout<<"Ordering is possible."<<endl;
}
else
{
cout<<"The door cannot be opened."<<endl;
}
}
return 0;
}