【洛谷 P1127】词链

题目描述

如果单词X的末字母与单词Y的首字母相同,则X与Y可以相连成X.Y。(注意:X、Y之间是英文的句号“.”)。例如,单词dog与单词gopher,则dog与gopher可以相连成dog.gopher。

另外还有一些例子:

dog.gopher

gopher.rat

rat.tiger

aloha.aloha

arachnid.dog

连接成的词可以与其他单词相连,组成更长的词链,例如:

aloha.arachnid.dog.gopher.rat.tiger

注意到,“.”两边的字母一定是相同的。

现在给你一些单词,请你找到字典序最小的词链,使得这些单词在词链中出现且仅出现一次。

输入格式:

第一行是一个正整数n(1 ≤ n ≤ 1000),代表单词数量。
接下来共有n行,每行是一个由1到20个小写字母组成的单词

输出格式:

只有一行,表示组成字典序最小的词链,若不存在则只输出三个星号“*”。

数据范围:

对于100%的数据,有n≤1000。

补昨天的博客…

首先,这是一道图论题…

这种单词接龙样子的一般都是图论题,相信大家都知(bei)道(keng)了(guo)吧…

如果以单词作为点,能连的单词建边,那好像就是一个裸的哈密顿路了?

不过连边的顺序要注意,先连字典序小的,再连字典序大的,这样能够保证找到的第一条哈密顿路就是答案。

所以要用string类型,先排序,用排名代替单词建图。

然后我就开开心心地敲了一个哈密顿路…

30分…

不光有TLE还有WA…

吓得我扫了好几遍代码,终于发现了华点…

我先把单词从小到大排序,然后对于每一个单词,先连小的再连大的…

也就是:

for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
        if(i!=j&&a[i][len[i]-1]==a[j][0])
            add(i,j);

我天真地以为这样连就能先访问小的再访问大的…

可是因为head数组指向所连的最后一条边,所以先连的边最后访问啊!

又sb了…不过数据很水还给了三十分…

所以应该把第二层j倒着循环,改完了又交了一遍。

T了两个点..

这道题哈密顿路应该不会错啊…可是怎么优化?

经过冥(fan)思(kan)苦(ti)想(jie)后,我仿若醍醐灌顶…

可以用欧拉路来优化!

如果把二十六个字母作为点,每个单词首尾字母连一条边,那不就是跑欧拉路吗!

不过不能纯跑欧拉路,因为建边的时候不能按照字典序建边(不一定指向a就比指向b小),所以不能保证找到的第一条欧拉路就是答案。

但可以用来查找从那个点开始搜索。

因为如果不存在哈密顿路,那也就不存在欧拉路(这两个路本质上好像没什么区别),如果存在哈密顿路,那么以哈密顿路的开头单词的首字母一定能跑出欧拉路来。

所以我们就可以先判断从哪些字母出发可以跑出欧拉路,再从以这些字母为首的单词开始跑哈密顿路。

虽然理论时间复杂度好像不变,但加了这个优化就跑到12ms了。

代码如下:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdlib>
using namespace std;
struct edge{
    int to,next;
}ed[1000001];
int head[1001]={0};
string a[1001];
int b[30]={0};
bool vl[1001]={0};
bool vis[1001]={0};
int ans[1001]={0};
int len[1001];
int size=0;
int cnt=0,n;
void add(int from,int to)
{
    size++;
    ed[size].to=to;
    ed[size].next=head[from];
    head[from]=size;
}
void print()
{
    for(int i=1;i<cnt;i++)
    {
        cout<<a[ans[i]];
        printf(".");
    }
    cout<<a[ans[cnt]];
    exit(0);
}
void dfs(int u)
{
    vl[u]=vis[u]=1;
    ans[++cnt]=u;
    for(int i=head[u];i;i=ed[i].next)
    {
        int v=ed[i].to;
        if(!vis[v]) dfs(v);
    }
    if(cnt==n)
    {
        print();
    }
    vis[u]=0;
    cnt--;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++)
    {
        len[i]=a[i].size();
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=n;j>=1;j--)
        {
            if(i!=j&&a[i][len[i]-1]==a[j][0])
            {
                add(i,j);
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        b[a[i][0]-'a']++;
        b[a[i][len[i]-1]-'a']--;
    }
    int c1=0,c_1=0,s;
    for(int i=0;i<26;i++)
    {
        if(b[i]==1) c1++,s=i;
        else if(b[i]==-1) c_1++;
    }
    if(c1==1&&c_1==1)
    {
        for(int i=1;i<=n;i++)
        {
            if(a[i][0]-'a'==s)
                dfs(i);
        }
    }
    else if(c1==0&&c_1==0)
    {
        for(int i=1;i<=n;i++)
            dfs(i);
    }
    printf("***");
    return 0;
}

这个算法好像有个漏洞,就是单纯的sort排序能否保证第一条路径就是答案?

例如abc和abcb,用string比较结果是abc小于abcb,但是如果后面接单词的话就是abc.c…和abcb.b…,显然第二个又更小了…

咸鱼的想法:前面字母都一样最后一个字母不一样几率辣么小忽略不就行辣

也许字典序并不是我理解的那样吧…反正a了就好。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值