转载题解 单词游戏

题解
题目描述
原题来自:2009 Multi-University Training Contest 12 - Host by FZU
有 N NN个盘子,每个盘子上写着一个仅由小写字母组成的英文单词。你需要给这些盘子安排一个合适的顺序,使得相邻两个盘子中,前一个盘子上单词的末字母等于后一个盘子上单词的首字母。请你编写一个程序,判断是否能达到这一要求。如果能,请给出一个合适的顺序。

输入格式
多组数据。第一行给出数据组数T TT ,每组数据第一行给出盘子数量 N NN,接下去 N NN 行给出小写字母字符串,一种字符串可能出现多次。

输出格式
若存在一组合法解输出Ordering is possible.,否则输出The door cannot be opened.。

样例
样例输入
3
2
acm
ibm
3
acm
malform
mouse
2
ok
ok
1
2
3
4
5
6
7
8
9
10
11
样例输出
The door cannot be opened.
Ordering is possible.
The door cannot be opened.
1
2
3
数据范围与提示
1 ≤ 1 0 5 , 0 ≤ M ≤ 2 × 1 0 5 , 1 ≤ a , b ≤ N 1 \le 10^5,0 \le M \le 2 \times 10^5,1 \le a,b \le N1≤10 
5
 ,0≤M≤2×10 
5
 ,1≤a,b≤N

题解
首先这道题的题目就是求一个可以不重复,不遗漏地遍历每一个盘子,要求是前后两个盘子的尾、首字母要相同

根据刚才的描述,我们可以发现这和欧拉通路很像,于是初始思路就来了:

依次判断每一个盘子,若两个盘子满足要求,则在这两个盘子间连一条边,最后判一下有没有欧拉通路就OK了。

但是这种思路有一些问题,因为我们像上面这么做,就相当于要求每两个盘子间的边都要遍历到(欧拉通路的定义嘛),然鹅题目只要求遍历完每个盘子就行了,所以我们就想到可不可以将盘子转化成边再判通路

我们这样想:盘子连接的条件就是首尾字母相同嘛,如果把盘子搞成了一条边,那么对于这条边,我们肯定需要且只需知道这个盘子的首位即可,于是把首尾设成边的起始和结尾即可,那么还有一个问题,这么建边后因该怎么判断两盘子可以相连,这不很简单吗,边的结尾连接另一条边的开始的条件就为这两个字母相等就好了,因为共有26个字母,所以我们最多只需要26个节点就好了


比如样例的第二组:

3
acm
malform
mouse
1
2
3
4
我们画出来的图就是这样的(边上的单词只是为了理解方便,实际实现时不用存),显然存在欧拉通路

我们再来一个不行的案例:

3
tan
tai
nb
1
2
3
4


在这张图中就不存在欧拉回路,所以答案也肯定是无解的,事实上,读者也可以自行枚举一下,确实是不行的

有了这些,我们就可以写出代码,注意还要判断一下是否连通,不连通也是无解的

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
const int MAXN=100005;
char a[MAXN];
int in[35];
int out[35];
int fa[35];
int find(int x){//查找爸爸(并查集基本操作) 
    if(fa[x]==x) return x;
    return fa[x]=find(fa[x]);
}
void mer(int u,int v){//合并(并查集基本操作) 
    int fu=find(u);
    int fv=find(v);
    if(fu!=fv){
        fa[fu]=fv;
    }
}
int main(){
    int t;
    scanf("%d",&t);
    int n;
    while(t--){
        scanf("%d",&n);
        memset(in,0,sizeof(in));
        memset(out,0,sizeof(out));
        for(int i=1;i<=26;i++){//并查集初始化 
            fa[i]=i;
        }
        for(int i=1;i<=n;i++){
            scanf("%s",a+1); 
            int u=a[1]-'a'+1;
            int len=strlen(a+1);
            int v=a[len]-'a'+1;
            in[v]++;//统计入度 
            out[u]++;//统计出度 
            mer(v,u);
        }
        int fath=-1;
        int cnt=0; 
        for(int i=1;i<=26;i++){//判连通 
            if(((in[i] || out[i]) && find(i)==i) || abs(in[i]-out[i])>1){ 
                cnt++;
            }
        }
        if(cnt>1){
            printf("The door cannot be opened.\n");
            continue;
        }
        int cnti=0,cnto=0;
        for(int i=1;i<=26;i++){//欧拉通路板子 
            if(in[i]-out[i]==1){
                cnti++;
            }
            if(in[i]-out[i]==1){
                cnto++;
            }
        }
        if(cnti==cnto==1){
            printf("Ordering is possible.\n");
        }
        else{
            printf("The door cannot be opened.\n");
        }
    }
return 0;
}


————————————————
版权声明:本文为CSDN博主「Fool-Fish」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/tanfuwen_/article/details/108129217

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值