【C++心路历程32】【NOIP2000】单词接龙【图论爆搜最长链】

【问题描述】

  单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母(不是单词),请你计算以这个字母开头的长度最长的“龙”,每个单词最多在“龙”中出现两次。
  要注意的是,两个单词接龙规则如下:
  1、如果第一个单词的后面的连续若干字母与第二个单词前面的连续若干字母依次相同,则这两个单词可以接龙,例如 beast 和 astonish ,如果接成一条龙则变为 beastonish。
  2、两个单词接成一条龙后,龙的长度要比两个单词的长度都长。例如单词 at 和 atide 接成一条龙后为atide,龙的长度与第二个单词相同,所以这两个单词不能接龙。而单词ababab和ababab,可以接成ababababab,但不能为ababab。
  3、接龙时,如果两个单词接龙后的单词有多种情况,要尽可能保证接龙后龙的长度最长。例如cabab和ababc,一定要接成cabababc,为而不是cababc。

【输入格式】

  输入的第一行为一个单独的整数n 表示单词数,以下n 行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在.

【输出格式】

  只需输出以此字母开头的最长的“龙”的长度

【输入样例】

5
at
touch
cheat
choose
tact
a

【输出样例】

23

【样例解释】

  最长的接龙是下面几个单词进行:
    at -> touch -> cheat -> tact -> tact -> touch -> choose
  连成长度为23的“龙”: atoucheatactactouchoose

【数据范围】

  50%的数据有:n<=5;
  100%的数据有:n<=10,每个单词的长度不超过40。
【分析】
1、暴力枚举各个单词的排列,并计算长度取最大值 .O(n!)
2、可以转化为图论,求有向图最长路径。在n^2的时间复杂度内连边单词,对于单词(a,b),边权为b的长度减去a,b重合长度,这样即可转化为一张有向图。 对于最后的起首字母做相同处理即可。
实现技巧:当然可以每个单词复制一遍,但凭空增加时间复杂度。可以在dfs时将vis标记数组上限改为2,这正好对应了“每个单词走访两遍”。

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=105;
char a[55][55],b[55];
int n,np=0,first[maxn],ans=100000,vis[maxn];
struct edge{
    int to,next,w;
}E[maxn<<1];
void addedge(int u,int v,int w)
{
    E[++np]=(edge){v,first[u],w};
    first[u]=np;
//  printf("%d %d %d\n",u,v,w);
}
void dfs(int i,int l)
{
    vis[i]++;//技巧
    ans=max(ans,l);
    for(int p=first[i];p;p=E[p].next)
    {
        int j=E[p].to,w=E[p].w;
        if(vis[j]==2) continue;
        dfs(j,l+w);
    }
    vis[i]--;
}
int check(int i,int j)
{
    int lenj=strlen(a[j]);
    int leni=strlen(a[i]);
    int ans=0;
    int k;
    for(k=0;k+1int ok=1;
        for(int r=0;r<=k;r++)
        {
            if(a[i][leni-1-k+r]!=a[j][r]) {ok=0;break;}
        }
        if(ok){ ans=k+1;break;}
    }
    if(ans==0) return 0;
    return lenj-ans;    
}
void init()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",&a[i]);memcpy(a[i+n],a[i],sizeof(a[i]));
    }
    scanf("%s",a[2*n+1]);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    {
        int w=check(i,j);
        if(w) addedge(i,j,w);
    }
    for(int i=1;i<=n;i++)//最后一个点编号随意
    {
        if(a[i][0]==a[2*n+1][0]) addedge(2*n+1,i,strlen(a[i])-1);
    }
}
int main()
{
//  freopen("in.txt","r",stdin);
    init();     
    ans=0;
    dfs(2*n+1,1);
    printf("%d",ans);
    return 0;
}

你可能感兴趣的:(【C++心路历程32】【NOIP2000】单词接龙【图论爆搜最长链】)