UVA10129 单词 解题报告

来源:互联网 发布:淘宝网上代购可信吗 编辑:程序博客网 时间:2024/06/05 23:28
【问题描述】  
  
  有一些秘密的门包含着非常有趣的单词迷题, 考古学家队伍必须解决它们才能够打开大门。 因为没有其他方法能打开这些门, 所以解决那些迷题对我们非常重要。 


  在每个门上有很多个有磁力的n个盘子,盘子上面写着单词。 必须重新移动放置这些盘子,让它们形成一个队列:队列中,除了第一个单词,每个单词的开头和上一个单词的结尾字母一样。例如, motorola的后面可以接上acm。 


  你的任务是写一个程序, 读入一系列单词,然后计算确定它们是否有可能被排成这样的队列。 
 
    
 【输入格式】  
  
  第一行是测试数据组数T,每组数据的第一行是一个整数n,表示有n个盘子(编号为1..n),接下来的n行每行一个单词,表示盘子上的的单词。
 
    
 【输出格式】  
   
  每组数据输出一行,如果能排成题目要求的队列,则输出“Ordering is possible.”,否则输出“The door cannot be opened.”
 
    
 【输入样例】   
   
3
2
acm
ibm
3
acm
malform
mouse
2
ok
ok
 
    
 【输出样例】  
   
The door cannot be opened.
Ordering is possible.
The door cannot be opened.
 
    
 【数据范围】  
   
1<=n<=100000,每个单词最多包含1000个小写字母
 
    
 【来源】  
  
《算法竞赛》169页 , uva 10129


 解题思路:本题是典型的判断欧拉路径的题目。根据题意,把每个单词的首字母和尾字母看作图的顶点,从首字母到尾字母连一条有向边,因为该题顶点数最多为26(所有字母),所以可以选择用邻接矩阵来存储。记录每个顶点的入度和出度,根据欧拉路径的性质,所有的点要么入度等于出度,要么其中一个点入度等于出度+1(终点),另一个点出度等于入度+1(起点),其余点的入度等于出度。如果满足该性质则继续根据欧拉路径的性质判断该图是否基连通(忽略所有边的方向后,判定图是否只有一个连通分量),这时先找图的起点(不能选择任意点)进行DFS,如果该图所有点的入度都等于出度则任选一点进行DFS,看图上的点是否都被遍历到,若不是则该图没有欧拉路径。


#include<cstdio>#include<cstdlib>#include<iostream>#include<algorithm>#include<vector>#include<queue>#include<cstring>using namespace std;const int maxn=100005;int T,N;char s[1005];int g[30][30],vis[30],rd[30],cd[30];  //使用邻接矩阵进行存储void DFS(int i){vis[i]=1;for(int j=1;j<=26;j++){if(g[i][j]>0 && vis[j]==0)   DFS(j);}}int main(){freopen("48.in","r",stdin);//freopen("48.out","w",stdout);scanf("%d",&T);for(int i=1;i<=T;i++){scanf("%d",&N);memset(g,0,sizeof(g));memset(rd,0,sizeof(rd));memset(cd,0,sizeof(cd));for(int j=1;j<=N;j++){scanf("%s",s);int a=s[0]-'a'+1;  //将字母转化为数字int b=s[strlen(s)-1]-'a'+1;cd[a]++;rd[b]++;g[a][b]++;}int cnt1=0,cnt2=0;int ok1=1;for(int j=1;j<=26;j++)  //根据欧拉路径的性质进行判断{if(cd[j]!=rd[j]){if(cd[j]==rd[j]+1)  cnt1++;else if(rd[j]==cd[j]+1)  cnt2++;else {ok1=0;break;}}}if(cnt1>0 && cnt2>0 && cnt1+cnt2>2)  ok1=0;  //如果起点和终点不唯一则没有欧拉路径if(ok1==1){memset(vis,0,sizeof(vis));int ok3=0;for(int j=1;j<=26;j++)if(cd[j]==rd[j]+1)  //找该图的起点进行DFS遍历{DFS(j);ok3=1;break;}if(ok3==0)  //如果该图所有点入度等于出度(没有起点),则任选一点进行DFS遍历{for(int j=1;j<=26;j++)if(cd[j]>0)  {DFS(j);ok3=1;break;}}int ok2=1;for(int j=1;j<=26;j++){if((rd[j]>0 || cd[j]>0) && vis[j]==0)  //判断是否所有点被遍历完{ok2=0;break;}}if(ok2==1)  printf("Ordering is possible.\n");else  printf("The door cannot be opened.\n");}else  printf("The door cannot be opened.\n");}return 0;}


1 0