[问题描述]
Io和Ao在玩一个单词游戏。他们轮流说出一个仅包含元音字母的单词,并且后一个单词的第一个字母必须与前一个单词的最后一个字母一致。游戏可以从任何一个单词开始。
任何单词禁止说两遍,游戏中只能使用给定词典中含有的单词。
游戏的复杂度定义为游戏中所使用的单词的长度总和。
编写程序求出使用一本给定的词典来玩这个游戏所能达到的游戏最大可能复杂度。
数据规模限制:单词总数不超过16,单词长度不超过100。
分析:
集合的表示方法:将一个集合与一个二进制数对应,再将二进制数与十进制数对应。为了方便操作,单词的编号也可以从1…N,改成0…N-1。比如:集合[1,4,5]->集合[0,3,4]->11001(2)->2^0+2^3+2^4=25。本题目中的档次总数不超过16,就可以设置长度为16的二进制比特串。这样操作不但节省了空间,而且在进行集合操作时可以用位操作,又节省了时间。
//============================================================================
// Name : word.cpp
// Author : Gecko
// Version : 1.0
// Copyright : Your copyright notice
// Description : 记忆搜索,以单词为例。本体技巧除了记忆搜索外还有使用二进制表示集合关系
//============================================================================
#include <stdio.h>
#include <string.h>
const int maxn = 16;
const int pow[17] = {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536};
const int maxtotal = 65535;
char *w[maxn];//单词
int a[maxn][maxn];//单词之间的关系
int c[maxn][maxtotal];//最优值
int len[maxn];//单词长度
int n,i,j,ans,temp,total;
int work(int i,int j)
{
int j1,k,temp;
if (c[i][j])
return c[i][j];
j1 = j-pow[i];
for(k=0;k<n;k++)
{
if(a[i][k] && pow[k] && j)
{
temp = work(k,j1)+len[i];
if (temp>c[i][j])
c[i][j] = temp;
}
}
return c[i][j];
}
int main()
{
scanf("%d",&n);
for (i=0;i<n;i++)
{
scanf("%s",w[i]);
len[i] = strlen(w[i]);
}
memset(a,sizeof(a),0);
for(i=0;i<n;i++)
for(j=0;j<n;j++)
if(i!=j && (w[i][len[i]] == w[j][1]))
a[i][j] = 1;
total = pow[n]-1;
memset(c,sizeof(c),0);
for (i=0;i<n;i++)
{
c[i][pow[i]] = len[i];
}
ans = 0;
for(i=0;i<n;i++)
{
for(j=0;j<=total;j++)
{
if (pow[i] && j)
{
temp = work(i,j);
if (temp>ans)
{
ans = temp;
}
}
}
}
printf("%d",ans);
return 0;
}