HDU 1116 || POJ 1386 || ZOJ 2016 Play on Words (欧拉回路+并查集)

题目链接

题意 : 有很多门,每个门上有很多磁盘,每个盘上一个单词,必须重新排列磁盘使得每个单词的第一个字母与前一个单词的最后一个字母相同。给你一组单词问能不能排成上述形式。

思路 :把每个单词看成有首字母指向尾字母的有向边,每个字母看成一个点,题中要求等效于判断图中是否存在一条路径经过每一条一次且仅一次,就是有向欧拉通路。统计个顶点的出入度,如果每个点的出入度都相同,那就是欧拉回路,如果有两个奇数度,那就是欧拉通路,除此之外,都不能满足要求。还有别忘了判断是否连通,此时用到并查集,图中所有的边(u,v),如果u!=v且属于不同的连通分量,就合并。欧拉回路的基本定理及概念。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 
 5 using namespace std ;
 6 
 7 char word[1001] ;
 8 int father[27],out[27],in[27] ,vis[30];
 9 
10 int find_(int x)
11 {
12     if(father[x] != x)
13         father[x] = find_(father[x]) ;
14     return father[x] ;
15 }
16 
17 void mergee(int a,int b)
18 {
19     if(find_(a) != find_(b))
20         father[find_(a)] = find_(b) ;
21 }
22 
23 void Init()
24 {
25     memset(out,0,sizeof(out)) ;
26     memset(in,0,sizeof(in)) ;
27     for(int i = 0 ; i < 26 ; i++)
28         father[i] = i ;
29     memset(vis,0,sizeof(vis)) ;
30 }
31 
32 int main()
33 {
34     int T ;
35     cin >> T ;
36     int n ;
37     while(T--)
38     {
39         cin >> n ;
40         Init() ;
41         while(n -- )
42         {
43             scanf("%s",word) ;
44             int u = word[0]-'a' ;
45             int v = word[strlen(word)-1]-'a' ;
46             mergee(u,v) ;
47             out[u] ++ ;
48             in[v] ++ ;
49             vis[u] = vis[v] = 1 ;
50         }
51         int cnt = 0,cnt1 = 0 ,cnt2 = 0 ;
52         for(int i = 0 ; i < 26 ; i++)
53         {
54             if(vis[i] && father[i] == i)
55             {
56                 cnt ++ ;
57             }
58         }
59         if(cnt > 1)
60         {
61             puts("The door cannot be opened.") ;
62             continue ;
63         }
64         bool flag = true ;
65         for(int i = 0 ; i < 26 ; i++)
66         {
67             if(vis[i] && out[i] != in[i])
68             {
69                 if(out[i]-in[i] == 1)
70                 {
71                     cnt1 ++ ;
72                     if(cnt1 > 1)
73                     {
74                         flag = false ;
75                         break ;
76                     }
77                 }
78                 else if(in[i]-out[i] == 1)
79                 {
80                     cnt2 ++ ;
81                     if(cnt2 > 1)
82                     {
83                         flag = false ;
84                         break ;
85                     }
86                 }
87                 else
88                 {
89                     flag = false ;
90                     break ;
91                 }
92             }
93         }
94         if(!flag) puts("The door cannot be opened.") ;
95         else puts("Ordering is possible.") ;
96     }
97     return 0 ;
98 }
View Code

 

转载于:https://www.cnblogs.com/luyingfeng/p/3877361.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值