poj 2337 Catenyms (欧拉路径+字典序最小)

Catenyms
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 8414 Accepted: 2212

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 last 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


思路:

建图+找欧拉路径。


感想:

这题难在保证答案字典序最小,注意‘.’的字典序最小,所以一个点到一个点有多条路径时要首先访问边(即单词)字典序小的(我是用set来处理的),然后点的访问顺序也要按字母顺序来(建边时处理一下就够了),这样就能保证整体字典序最小了。


代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <vector>
#include <set>
#define maxn 30
using namespace std;

int n,m,ans,cnt,num,xxc,sx,xxs;
bool vis[1005],app[maxn];
int pre[maxn];
int in[maxn],out[maxn];
int sta[1005];
char s[1005][30],ts[30];
set<string>ss[maxn][maxn];
string s1;
int pp[maxn];
struct Node
{
    int v,next;
} edge[1005];

void init()
{
    int i;
    for(i=0; i<=26; i++)
    {
        pre[i]=i;
    }
    num=0;
}
int sfind(int x)        // 查找时优化了路径
{
    while(pre[x]!=x) x=pre[x];
    return x;
}
void smerge(int a,int b)    // 合并时将深度小的集合合并到大的里面
{
    int x,y;
    x=sfind(a);
    y=sfind(b);
    if(x!=y)
    {
        pre[x]=y;
        num++;
    }
}
void init1()
{
    int i,j;
    memset(pp,0,sizeof(pp));
    memset(in,0,sizeof(in));
    memset(out,0,sizeof(out));
    memset(app,0,sizeof(app));
    memset(vis,0,sizeof(vis));
    for(i=0;i<26;i++)
    {
        for(j=0;j<26;j++)
        {
            ss[i][j].clear();
        }
    }
}
void addedge(int u,int v)
{
    cnt++;
    edge[cnt].v=v;
    edge[cnt].next=pp[u];
    pp[u]=cnt;
}
bool isok()
{
    int i,j,t,u,v;
    if(num!=xxc-1) return false ;
    u=v=0;
    for(i=0; i<26; i++)
    {
        if(in[i]==out[i]) continue ;
        t=in[i]-out[i];
        if(t==1) u++;
        else if(t==-1) v++,sx=i;
        else return false ;
    }
    if(u==1&&v==1) return true ;
    else if(u==0&&v==0)
    {
        for(i=0;i<26;i++)
        {
            if(app[i])
            {
                sx=i;
                break ;
            }
        }
        return true ;
    }
    return false ;
}
void dfs(int u)
{
    int i,j;
    for(i=pp[u];i;i=edge[i].next)
    {
        if(!vis[i])
        {
            vis[i]=1;
            dfs(edge[i].v);
        }
    }
    sta[++xxs]=u;
}
int main()
{
    int i,j,t,len,u,v,k;
    scanf("%d",&t);
    while(t--)
    {
        init();
        init1();
        scanf("%d",&n);
        cnt=xxc=0;
        for(i=1; i<=n; i++)
        {
            scanf("%s",s[i]);
        }
        for(i=1;i<=n;i++)   // 用STL用不好 只能自己写冒泡排序了
        {
            strcpy(ts,s[i]);
            k=i;
            for(j=i+1;j<=n;j++)
            {
                if(strcmp(ts,s[j])<0)
                {
                    k=j;
                    strcpy(ts,s[j]);
                }
            }
            strcpy(ts,s[i]);
            strcpy(s[i],s[k]);
            strcpy(s[k],ts);
        }
        for(i=1;i<=n;i++)  // 从大到小排序后倒着建边
        {
            len=strlen(s[i]);
            u=s[i][0]-'a';
            v=s[i][len-1]-'a';
            smerge(u,v);
            ss[u][v].insert(s[i]);
            if(!app[u]) app[u]=1,xxc++;
            if(!app[v]) app[v]=1,xxc++;
            out[u]++;
            in[v]++;
            addedge(u,v);
        }
        if(isok())    // 判断能否形成欧拉路径
        {
            xxs=0;
            dfs(sx);  // dfs找欧拉路径
            for(i=xxs;i>1;i--)
            {
                u=sta[i];
                v=sta[i-1];
                s1=*(ss[u][v].begin());  // 用到了set来保证字典序最小
                if(i==xxs) cout<<s1;
                else  cout<<"."<<s1;
                ss[u][v].erase(s1);
            }
            printf("\n");
        }
        else printf("***\n");
    }
    return 0;
}
/*
2
5
aa
ab
aba
ba
bb
3
ab
ba
abca

ans:
aa.ab.bb.ba.aba
ab.ba.abca
*/




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值