【洛谷 P1127】词链

来源:互联网 发布:linux命令创建多层目录 编辑:程序博客网 时间:2024/06/10 02:51

题目描述

如果单词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了就好。

原创粉丝点击