题目
Description
Io和Ao在玩一个单词游戏。
他们轮流说出一个仅包含元音字母的单词,并且后一个单词的第一个字母必须与前一个单词的最后一个字母一致。
游戏可以从任何一个单词开始。
任何单词禁止说两遍,游戏中只能使用给定词典中含有的单词。
游戏的复杂度定义为游戏中所使用的单词长度总和。
编写程序,求出使用一本给定的词典来玩这个游戏所能达到的游戏最大可能复杂度。
Input
输入文件的第一行,表示一个自然数N(1≤N≤16),N表示一本字典中包含的单词数量以下的每一行包含字典中的一个单词,每一个单词是由字母A、E、I、O和U组成的一个字符串,每个单词的长度将小于等于100,所有的单词是不一样的。。
Output
输出文件仅有一行,表示该游戏的最大可能复杂度。
Sample Input
5 IOO IUUO AI OIOOI AOOI
Sample Output
16
思路
这是一道状压$dp$的题;
题目可以转化为将允许使用的单词连接的最大长度(连接条件有限制);
我们设$dp[i][k]$ 表示连到第$i$ 个单词,$k$是连接好的单词的集合,$dp[i][k]$ 就是最大长度;
那么就可以预处理出所有单词的长度$a[i]$;
然后判断两个单词是否可以连接$f[i][j]$;
转移方程是: $dp[i][k]=max(dp[i][k],dp[j][k oplus (1<<(i-1))]+a[i])$
代码
#include<bits/stdc++.h> #define re register typedef long long ll; using namespace std; inline ll read() { ll a=0,f=1; char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();} while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();} return a*f; } ll n; ll a[20],f[20][20]; char s[20][101]; ll dp[20][1<<16]; int main() { memset(dp,-1,sizeof(dp)); n=read(); for(re ll i=1;i<=n;i++) { scanf("%s",s[i]+1);//读入 a[i]=strlen(s[i]+1);//预处理所有单词的长度 } for(re ll i=1;i<=n;i++) for(re ll j=1;j<=n;j++) { ll len=strlen(s[i]+1); if(s[i][len]==s[j][1]) f[i][j]=1;//判断两个单词是否可以连接 } for(re ll i=1;i<=n;i++) dp[i][1<<(i-1)]=a[i];//可以从每一个单词作为集合第一个数开始 for(re ll k=1;k<=(1<<n)-1;k++)//枚举集合状态 for(re ll i=1;i<=n;i++) { if(!(k&(1<<(i-1)))//判断 i 这个单词是否在集合里 continue; for(re ll j=1;j<=n;j++) if(f[j][i])//如果可以连 { if(i==j)//不能自己连自己 continue; if(k&(1<<(j-1)))//j 是否在集合里 dp[i][k]=max(dp[i][k],dp[j][k^(1<<(i-1))]+a[i]);//转移方程 } // cout<<i<<" "<<k<<endl; // cout<<dp[i][k]<<endl; } ll ans=-1; for(re ll k=1;k<=(1<<n)-1;k++) for(re ll i=1;i<=n;i++) ans=max(ans,dp[i][k]);//可以从任何一个状态或单词结束 printf("%lld ",ans);//输出ans //return 0; }