UESTC Training for Graph Theory——c、Catenyms

Catenyms

Time Limit: 1000 ms Memory Limit: 65536 kB Solved: 148 Tried: 1114

Description

A catenym is a pair of words separated by a period such that the last letter of the first word is the same as the first letter of the second. For example, the following are catenyms: 

dog.gopher
gopher.rat
rat.tiger
aloha.aloha
arachnid.dog

A compound catenym is a sequence of three or more words separated by periods such that each adjacent pair of words forms a catenym. For example, 

aloha.aloha.arachnid.dog.gopher.rat.tiger 

Given a dictionary of lower case words, you are to find a compound catenym that contains each of the words exactly once.

Input

The first line of standard input contains t, the number of test cases. Each test case begins with 3 <= n <= 1000 - the number of words in the dictionary. n distinct dictionary words follow; each word is a string of between 1 and 20 lowercase letters on a line by itself.

Output

For each test case, output a line giving the lexicographically least compound catenym that contains each dictionary word exactly once. Output "***" if there is no solution.

Sample Input

2
6
aloha
arachnid
dog
gopher
rat
tiger
3
oak
maple
elm

Sample Output

aloha.arachnid.dog.gopher.rat.tiger
***

Source

Waterloo local 2003.01.25

 

/*算法思想:
  给几个单词,要求用完所有的单词一遍进行单词接龙,并且使接得的字符串的字典序最小
  我们可以把每个单词抽象成一条边,边的起点是单词的第一个字符,边的终点是单词的最后一个字符
  边的权值就是这个单词,这样,我们就构造出了一个图,问题就转化成了求一条这个图的欧拉路径,使
  得欧拉路径上的单词拼接起来的字符串的字典序最小。
  首先,我们要判断这个图是不是可以存在一个欧拉路径,使得我们能够不重复的访问完所有的边。
  判断的方法就是统计图中所有节点的出度入度,要是出度入度不等,不存在,要是所有的点的出度均等于
  入度,那么就找一个字典序最小的点作为起始点。
  然后,我们需要判断图是不是只有一个联通块,只有在一个联通块的前提下,我们才能正确的找出欧拉路径
  最后就是找欧拉路径了,需要对单词进行排序,排序的规则是按照单词的字典序从大到小排序,因为最终
  我们输出答案的时候是逆着输出的,这样才能保证输出的路径满足字典序最小的规则。
*/

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 2000
using namespace std;
struct data1
{
    char word[30];
} s[N],ans[N];
struct data2
{
    int st,en;
    char val[30];
    int next;
} edge[N];
int n,tot,st_id;
int in[30],out[30],head[30],fa[30];
bool vs[N],use[30];
void make_set()
{
    for(int i=0;i<30;i++)
        fa[i]=i;
}
int find_set(int x)
{
    if(fa[x]!=x) fa[x]=find_set(fa[x]);
    return fa[x];
}
void merge_set(int x,int y)
{
    fa[y]=x;
}
void add_edge(int st,int en,char val[])  //加边
{
    in[en]++;
    out[st]++;
    use[st]=use[en]=true;
    edge[tot].st=st;
    edge[tot].en=en;
    strcpy(edge[tot].val,val);
    edge[tot].next=head[st];
    head[st]=tot++;
    int f1=find_set(st);
    int f2=find_set(en);
    if(f1!=f2) merge_set(f1,f2);
}
bool cmp(data1 a,data1 b)  //比较函数
{
   if(strcmp(a.word,b.word)>0) return true;
   else return false;
}
bool judge()  //判断图中是否存在一条欧拉路径
{
    st_id=-1;
    int sum1=0,sum2=0,set_num=0;
    for(int i=0;i<26;i++)
    if(use[i])
    {
        if(find_set(i)==i) set_num++;
        if(set_num>1) return false;
        if(in[i]-out[i]==1) sum2++;
        else if(in[i]-out[i]==-1)
        {
            sum1++;
            st_id=i;
        }
        else if(in[i]!=out[i]) return false;
    }
    if(set_num!=1) return false;  //如果不止一个联通块,不存在
    if(!((sum1==1 && sum2==1) || (sum1==0 && sum2==0))) return false;  //通过点的入度出度判断是否存在
    if(st_id==-1)  //如果是欧拉回路,我们要找一个最小的点作为起始点
    {
        for(int i=0;i<26;i++)
            if(use[i] && out[i]>0)
            {
                st_id=i;
                return true;
            }
    }
    if(sum1==1 && sum2==1) return true;
    return false;
}
void eular(int v,int id)  //dfs找欧拉回路
{
    for(int pos=head[v];pos!=-1;pos=edge[pos].next)
        if(!vs[pos])
        {
            vs[pos]=true;
            eular(edge[pos].en,pos);
        }
    if(id!=-1) strcpy(ans[++tot].word,edge[id].val);
}
int main()
{
    int t;
    scanf("%d",&t);
    for(int ca=1;ca<=t;ca++)
    {
        memset(in,0,sizeof(in));
        memset(out,0,sizeof(out));
        memset(head,-1,sizeof(head));
        memset(edge,0,sizeof(edge));
        memset(use,0,sizeof(use));
        tot=1;
        make_set();
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%s",s[i].word);
        sort(s+1,s+n+1,cmp);
        for(int i=1;i<=n;i++)
        {
            int st=s[i].word[0]-'a';
            int en=s[i].word[strlen(s[i].word)-1]-'a';
            add_edge(st,en,s[i].word);
        }
        if(judge())
        {
            tot=0;
            memset(ans,0,sizeof(ans));
            memset(vs,0,sizeof(vs));
            eular(st_id,-1);
            for(int i=tot;i>=1;i--)
            {
                printf("%s",ans[i].word);
                if(i!=1) printf("."); else printf("\n");
            }
        }
        else printf("***\n");
    }
    return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值