nyoj 单词拼接(并查集判断连通性+欧拉路径)

这题还是比较难的。


首先建图方面,如果单纯的把单词作为点,能拼接的关系作为边,那么就是哈密顿图(每个点仅能走一次),难度比较大。

换一种思路,就是把每个单词看成一条有向边,由该单词的首字母指向尾字母。

那么这题便是欧拉图的问题了。


本质上采用的还是搜索,但是为了快速得到字典序最小的欧拉路径,首先要对单词集进行排序。

排完序后,用边集数组存图;再通过计算各点的出度与入度,同时判断基图(不考虑边的方向的图)的连通性,判断是否存在欧拉回路或欧拉通路。

如果存在欧拉回路,那么就从0开始搜索;

如果存在欧拉通路,那么就从“出度=入度+1”的点开始搜索。


参考代码如下:

边集数组存图+并查集判断连通性

 

 
#include<iostream>
using namespace std;
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<algorithm>

struct Enode{
       int u;
       int v;
       string s;
};

Enode edge[1010];
string s[1010];
int n,m,fa[1010];
int flag[1010];
int path[1010];
int visit[1010];

int root(int x)
{
    if (fa[x]==x) return x;
    return fa[x]=root(fa[x]);
}

void uset(int x,int y) 
{
     int a=root(x);
     int b=root(y);
     if (a!=b)
        fa[b]=a;
}

//并查集判断连通性 
int judge()
{
    int t=0;
    for (int i=0;i<26;++i)
        if (flag[i] && fa[i]==i) t++;
    return t == 1;
}

void find(int u)
{   
    for (int i=0;i<n;++i)
        if (!visit[i] && edge[i].u==u)
           {
           visit[i]=1;
           find(edge[i].v);
           path[m++]=i;
           }
}

int main()
{
    int T;
    cin >> T;
    while (T--)
          {    
          memset(visit,0,sizeof(visit));          
          memset(flag,0,sizeof(flag));
         // memset(edge,0,sizeof(edge));
          memset(path,0,sizeof(path));
          memset(fa,0,sizeof(fa));
          
          cin >> n;
          int i;
          for (i=0;i<n;++i)
              {
              cin >> s[i]; 
              }
          sort(s,s+n);
          
          for (i=0;i<26;++i) fa[i]=i;
          
          int in[30],out[30];
          memset(in,0,sizeof(in));
          memset(out,0,sizeof(out));
          
          for (i=0;i<n;++i)
              {
              int u=s[i][0]-'a';
              int v=s[i][s[i].length()-1]-'a';
              edge[i].u=u;
              edge[i].v=v;
              edge[i].s=s[i];
              in[v]++;
              out[u]++;
              flag[u]=flag[v]=1;
              uset(u,v);
             // cout << edge[i].s << endl; 
              }
          int start=edge[0].u;
          int cnt1=0,cnt2=0;
            
          //这段代码感觉很经典。。 
          for (i=0;i<26;++i)
          {
              if (in[i]==out[i]) continue;
              else 
              if (in[i]==out[i]+1) 
                 {
                 cnt1++;
                 } 
                 else if (in[i]==out[i]-1)
                      {
                      cnt2++;
                      start=i; 
                      }
                 else break;
                 
          }    
          
          if (i==26 && (cnt1==cnt2||(cnt1<2)) && judge())
             {
             m=0;   
             find(start);
             for (i=m-1;i>0;--i)
                 cout << edge[path[i]].s << ".";
             cout << edge[path[0]].s << endl;
             }
             
             else cout << "***" << endl;
                       
          }
    return 0;
}
        



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值