[POJ2337]

Catenyms
http://poj.org/problem?id=2337

题目大意:

单词首尾相接,问是否可以把所有单词串成一串,如果可以打印出来。

判断是否可以完成其实很简单,普通的有向图判是否存在欧拉路径(degree!=0的点没有,或者之后两个且一个1一个-1)

这一次要打印字典序最小的欧拉路径。

很容易想到先把所有单词读到本地,做一次排序,再将所有单词接到结点a..z上。先特殊判断是否存在欧拉路径(基图连通性判断,degree判断)。

之后用一次深搜打印欧拉路径。

WA了很多次,有两个要注意点,在dfs函数里头:

1、从一个点出发,是可以找到很多环的,多以dfs一次以后不要return,继续找下一个环

2、将所有单词接到结点上要注意是字典序顺序还是逆序的(我习惯加边的方法加进去以后单词变成字典序逆序了),可能要用一个栈来暂存答案,而不能直接输出。


源代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <limits.h>
#include <algorithm>
using namespace std;
#define maxn 26

struct edge{
    int v, next, id;
    bool vst;
}e[1010];

struct word{
    char str[30];
}word[1010];

int n, cnt;
int g[maxn], stack[1010];

bool cmp(struct word a, struct word b)
{
    return strcmp(a.str, b.str)>0;
}

void MakeSet(int f[], int a)
{
    f[a]=a;
}
int Find(int f[],int a)
{
    if(f[a]==a) return a;
    return f[a]=Find(f,f[a]);
}
bool Same(int f[],int a,int b)
{
    return Find(f,a)==Find(f,b);
}
void Union(int f[],int a,int b)
{
    f[Find(f,a)]=Find(f,b);
}
    
int init()
{
    int odd, t1, t2;
    int f[26], deg[26];
    bool show[26];
    
    scanf("%d", &n);   
    for (int i=0; i<n; i++)
        scanf("%s", word[i].str); 
    sort(word, word+n, cmp);
    
    memset(g, 255, sizeof(g));          //边 
    memset(deg, 0, sizeof(deg));        //度数 
    memset(show, 0, sizeof(show));
    
    for (int i=0; i<26; i++)
        MakeSet(f, i);
    
    for (int i=0, j=0; i<n; i++)
    {
        t1=word[i].str[0]-'a';
        t2=word[i].str[strlen(word[i].str)-1]-'a';
        deg[t1]--;
        deg[t2]++;
        show[t1]=show[t2]=true;
        Union(f, t1, t2);
        e[j].v=t2; e[j].id=i; e[j].vst=false; e[j].next=g[t1]; g[t1]=j++;
    }
    
    for (int i=0; i<26; i++)                //判连基图的连通性 
        if (show[i])
        {
            for (int j=i+1; j<26; j++)
                if (show[j] && !Same(f, i, j)) 
                    return -1;
            break;
        }
        
    odd=0;
    for (int i=0; i<26; i++)
        if (show[i] && deg[i]!=0)
        {
            if (deg[i]!=1 && deg[i]!=-1) return -1;
            if (odd==0) t1=i;
            else if (odd==1) t2=i;
            odd++;
            if (odd>2) return -1;
        }
        
    if (odd==1) return -1;
    else if (odd==2)                        //不存在欧拉回路,只存在欧拉路径 
    {
        if (deg[t1]+deg[t2]!=0)
            return -1;
        if (deg[t1]==-1)
            return t1;
        else
            return t2;
    }
    
    for (int i=0; i<26; i++)                //存在欧拉回路,字典序最小的点开始 
        if (show[i]) return i;
    
    return -1;
}

void dfs(int k, int eid)
{
    for (int i=g[k]; i!=-1; i=e[i].next)
        if (e[i].vst==false)
        {
            e[i].vst=true;
            dfs(e[i].v, i);
        }
    if (eid!=-1)
        stack[cnt++]=e[eid].id;
}

int main()
{
    int cs, t;
    scanf("%d", &cs);
    while (cs--)
    {
        t=init();
        if (t==-1)
        {
            printf("***\n");
            continue;
        }
        else
        {
            cnt=0;
            dfs(t, -1);
            printf("%s", word[stack[cnt-1]].str);
            for (int i=cnt-2; i>=0; i--)
               printf(".%s", word[stack[i]].str); 
            printf("\n");
        }
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值