1386 Play on Words

POJ1386 Play on Words

原题链接

原题大意

一个神秘的大门包含一个非常有趣的单词拼图,考古学家团队必须解决这个问题才能打开大门。门上吸附着一些盘子,每个盘子上有一个单词,必须按照规定排列这些盘子,规则是每个单词的开头与上一个单词的结尾字母相同。

胡乱分析

可以将每个单词的首字母和尾字母看作节点,例如 " a c m " "acm" "acm",可以看作一条节点 a a a指向节点 b b b的路径,形成一个有向图,若所有单词都按照规则可以连接在一起,那么就形成了欧拉通路,也就是半欧拉图。再利用并查集检查图的连通性,那么问题就解决了。
所以问题就演变成了判断图是否是半欧拉图+图是否是连通的。
如何判断半欧拉图
并查集判断图的连通性
将每个单词的首位字母看作节点,将每个字母转换为对应的数字,存放在数组中,然后计算每个节点的度,若是首字母,出度加一,若是尾字母,入读加一。再根据半欧拉图性质中的节点出度与入读的关系,筛选它是否是半欧拉图,再判断图是否连通。
若图连通+是半欧拉图,输出Ordering is possible.
否则输出The door cannot be opened.

代码

//搬运的,侵删
//原文链接:https://blog.csdn.net/qq_40046426/article/details/97398569
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <stack>
#include <queue>
#include <map>
#include <vector>
#define ll long long
#define Max 100005
using namespace std;
string s;
int n,m;
int vis[30],fa[30];//vis判断字母是否出现,fa数组用于并查集
int out[30],in[30];//存每个字母的入度和出度
struct Node
{
    int u,v;
}edge[Max];
void init()
{
    memset(vis,0,sizeof(vis));
    memset(out,0,sizeof(out));
    memset(in,0,sizeof(in));
    for(int i=0;i<26;i++)fa[i]=i;
}
int Find(int x)
{
    if(fa[x]==x) return x;
    return fa[x]=Find(fa[x]);
}
void mix(int x,int y)
{
    int fx=Find(x);
    int fy=Find(y);
    if(fx!=fy)
    {
        fa[fx]=fy;
    }
}
int If_connect()
{
    //合并
    for(int i=0;i<n;i++){
        int u=edge[i].u,v=edge[i].v;
        if(u!=v&&Find(u)!=Find(v))
        {
            mix(u,v);
        }
    }
    //判断连通性
    int f=-1,k;
    for(k=0;k<26;k++)
    {
        if(!vis[k]) continue;
        if(f==-1) f=k; //f记录第一个存在的字符
        else if(Find(f)!=Find(k)) break;//祖先不一样 说明不连通
    }
    if(k<26) return 0;
    return 1;
}
int main()
{
    // freopen("in.txt","r",stdin);
    // freopen("out.txt","w",stdout);
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--)
    {
        init();
        cin>>n;
        for(int i=0;i<n;i++)
        {
            cin>>s;
            int u=s[0]-'a',v=s[s.length()-1]-'a';
            edge[i].u=u,edge[i].v=v;
            vis[u]=vis[v]=1;
            out[u]++,in[v]++; // 出度  入度
            //每个字母当成一个点
        }
        bool flag=true;
        int cnt1=0,cnt2=0;
        for(int i=0;i<26;i++){
            if(!vis[i]) continue; 
            //入度和出度不等
            if(abs(out[i]-in[i])>1){flag=false;break;}
            if(out[i]-in[i]==1)//如果有必须有一个
            {
                cnt1++; 
                if(cnt1>1){flag=false; break; }
            }
            if(out[i]-in[i]==-1)//如果有必须有一个
            {
                cnt2++;
                if(cnt2>1) {flag=false;break; }
            }
        }
        /*
        出度与入度都相等;或者除两个顶点外
        其余顶点的出度与入度都相等,而这两个顶点中一个顶点的出度-入度==1,
        另一个出度-入度==-1;
        */
        if(cnt1!=cnt2) flag=false; 
        if(!If_connect()) flag=false;
        if(flag) cout<<"Ordering is possible."<<endl;
        else cout<<"The door cannot be opened."<<endl;
 
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值