Description
Input
Output
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.
题意:考古学家要打开一扇门,门上有许多磁质盘子,每个盘子上有仅由小写字母组成的一个单词,我们要将这些盘子按一定顺序排序,规定是一个单词的首字母必须是它的前一个单词的末字母,如果能行就输出 Ordering is possible.否则输出 The door cannot be opened.
给出T组测试数据,每组测试数据第一行有数字N,表示有N个盘子,接下来N行每行有一个单词。
思路:这道题,有一个很巧妙的转换,把点变成边。以26个字母作为顶点;对于每一个盘子,如果它的首字母为c1,末字母为c2,那么从c1向c2连一条有向边。然后判断图连通,再判断它是否为欧拉图。因为有重边,用边表建图时(在建图时加判断 用边表也可以)会超时,用邻接矩形比较好 因为只有26个点。
这种题实际上就是判断有向图的欧拉路的存在性。也就是对所给的所有单词,所有出现过的不同的字母就是图上的顶点,读入每一个单词,单词的首字母对应的点出度加1,末 字母对应的点入度加1,最后再来做判断。也就是所有字母对应的入度=出度 或是除了端点的字母各自的出入度相差1以外其余的顶点出度=入度,就满足条件->能构成链。 那么对于所有出现过的字母,只用判断只有一个的出度为入度加1而且只有一个的入度等于出度加1,或所有点的出度等于入度就可以了。当然首先图必须是连通的,这点很关键,这个可以用并查集来做。
通过图(无向图或有向图)中所有边一次且仅一次行遍图中所有顶点的通路称为欧拉通路,通过图中所有边一次且仅一次行遍所有顶点的回路称为欧拉回路。具有欧拉回路的图称为欧拉图(Euler Graph),具有欧拉通路而无欧拉回路的图称为半欧拉图。
欧拉回路要求边不能重复,结点可以重复. 笔不离开纸,不重复地走完所有的边,且走过所有结点,就是所谓的一笔画. 欧拉图或通路的判定: (1) 无向连通图G是欧拉图当且仅当G中不含奇数度顶点(G的所有结点度数为偶数)
(2) 无向连通图G是半欧拉图当且仅当G恰有两个奇数度的顶点; (3) 有向图D是欧拉图当且仅当D是强连通的并且每个结点的入度=出度 (4) 有向图D是半欧拉图当且仅当D是单向连通的且恰有两个奇度顶点,其中一个顶点入度比出度大1,另一个顶点出度比入度大1,其余每个顶点的入度=出度,
欧拉回路+并查集:
#include <iostream> #include <string.h> #include <math.h> #include <string> using namespace std; int indeg[26],outdeg[26],father[26],visit[26]; //indeg入度,outdeg出度,father用于并查集操作(集合的代表元素),visit表征字母是不是已经用过了 int setfind(int x) { if(father[x]==-1) return x; return father[x]=setfind(father[x]); } void setUnion(int u,int v) { int fu=setfind(u); int fv=setfind(v); if(fu!=fv) father[fv]=fu; } bool Judge()//判断是否为欧拉图 { int sum1=0,sum2=0,sum3=0,i; for(i=0;i<26;i++) { if(visit[i]==1&&father[i]==-1)//找祖先 sum1++; if(fabs(indeg[i]-outdeg[i]-0.0)==1) sum2++; else if(indeg[i]!=outdeg[i]) sum3++; } if(sum3>0||sum2>2||sum1>1) return false; else return true; } int main() { int T; cin>>T; while(T--) { int N,i; string word; memset(indeg,0,sizeof(indeg)); //初始化操作 memset(outdeg,0,sizeof(outdeg)); memset(visit,0,sizeof(visit)); memset(father,-1,sizeof(father)); cin>>N; for(i=0;i<N;i++) { cin>>word; int len=word.size(); int u=word[0]-'a';//单词首字母 int v=word[len-1]-'a';//单词末字母 visit[u]=visit[v]=1;//单词已经用过了 setUnion(u,v);//连边v->u outdeg[u]++;//单词首字母出度+1 indeg[v]++;//单词末字母入度+1 } if(Judge()) cout<<"Ordering is possible."<<endl; else cout<<"The door cannot be opened."<<endl; } return 0; }
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
欧拉回路+DFS
#include <iostream>
#include <string.h>
#include <math.h>
#include <string>
using namespace std;
int indeg[26],outdeg[26],map[26][26],st;
//indeg入度,outdeg出度,map[i][j]=1表明i到j可连
bool Judge()
{
int sum1=0,sum2=0,sum3=0,i,temp;
for(i=0;i<26;i++)
{
if(indeg[i]-outdeg[i]==1)
sum1++;//终点
if(outdeg[i]-indeg[i]==1)
{ sum2++; st=i;}//祖先即起点,这种情况是形成欧拉通路而不是欧拉回路
if(indeg[i]==outdeg[i]&&indeg[i]!=0)
temp=i;
if(fabs(indeg[i]-outdeg[i]-0.0)>1)
sum3++;
}
if(sum3>0||sum2>1||sum1>1)
return false;
if(sum2==0)//表示形成欧拉回路
st=temp;//起点的赋值
return true;
}
void DFS(int u)
{
for(int i=0;i<26;i++)
if(map[u][i])
{
map[u][i]=0;
DFS(i);
}
}
int main()
{
int T;
cin>>T;
while(T--)
{
int N,i,j;
string word;
memset(indeg,0,sizeof(indeg)); //初始化操作
memset(outdeg,0,sizeof(outdeg));
memset(map,0,sizeof(map));
cin>>N;
for(i=0;i<N;i++)
{
cin>>word;
int len=word.size();
int u=word[0]-'a';//单词首字母
int v=word[len-1]-'a';//单词末字母
map[u][v]=1;//连边
outdeg[u]++;//单词首字母出度+1
indeg[v]++;//单词末字母入度+1
}
bool flag=false;
if(Judge())
{
flag=true;
DFS(st);
for(i=0;i<26;i++)
for(j=0;j<26;j++)
if(map[i][j])//表示路径没有经过所有边,自然也就不是欧拉路
{
flag=false;
break;
}
}
if(flag)
cout<<"Ordering is possible."<<endl;
else
cout<<"The door cannot be opened."<<endl;
}
return 0;
}