题意:一群小孩(至少两个)围成一圈做游戏。每一轮从某个小孩开始往他左边或者右边传手帕。一个小孩拿到手帕后在手帕上写上自己的性别,男孩写B,女孩写G,然后按照相同的方向传递给下一个小孩,每一轮都可能在任何一个小孩写完后停止,现在游戏已经进行了n轮,已知n轮中每轮手帕上留下的字,问最少可能有几个小孩。
ACM的题果真好题。书上说的已经非常好了,首先把能被包含的串去掉,这些串对答案没有贡献。剩下的工作就是把这些串按一定顺序摆好,每个串可以正可以反,求出最多能重叠多少长度,然后用所有串总长度减去就行了。首先按照长度排序,再看它后面的串能不能把它包含,这样避免两个相同的串都被删。剩下的串中,因为是一个圈,为了dp的时候方便,我们把第一个串所在的位置定为第一个,它的方向定为正方向,所以默认第一个串正向且放在第一个,最后还要加上第一个串和最后一个串的重叠部分。
当去重后只剩下一个串的时候,当然不能直接输出长度,因为可能绕了好几圈。当然长度<=100sb枚举答案也可以,但是预处理的时候已经求出了它真包含的前缀,和自己真包含的后缀的最长重叠,用总长度减去就可以了。
注意至少有两人,所以答案求出来是1要输出2
第一个串已经规定好正向放在第一个了,dp的时候忽略掉就可以了
(写的时候脑抽以为状压不能刷表,还很惊讶刷表为什么能A,后来想了一下,一个集合的子集对应的二进制值肯定比它小,所以从小到大枚举状态刷表没有问题)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,A[20][2][20][2],dp[1<<16][20][2];
bool vis[1<<16][20][2];
struct Node{
char s[2][200];
int len;
bool del;
void Read(){
scanf("%s",&s[0]);
strcpy(s[1],s[0]);
int i; len=strlen(s[0]);
for (i=0;i<=(len-1)/2;i++) swap(s[1][i],s[1][len-i-1]);
del=0;
}
bool operator < (const Node& a) const{
if (a.del!=del) return del<a.del;
return len<a.len;
}
}T[30];
bool cont(char *a,char *b,int len1,int len2){
int i,j;
for (i=0;i<=len2-len1;i++){
for (j=0;j<len1;j++)
if (a[j]!=b[i+j])
break;
if (j>=len1) return 1;
}
return 0;
}
int Count(char *a,char *b,int len1,int len2){
int i,j,p=min(len1,len2)-1;
for (i=p;i>=0;i--){
for (j=0;j<i;j++)
if (a[j]!=b[len2-i+j])
break;
if (j>=i) return i;
}
return 0;
}
int Dfs(int x,int num,int d){
if (x==1) return 0;
int &ans=dp[x][num][d];
if (vis[x][num][d]) return ans;
vis[x][num][d]=1;
ans=0;
if (x==(1<<(num-1))) return ans=A[num][d][1][0];
int i,j;
for (i=2;i<=n;i++)
for (j=0;j<2;j++)
if (i!=num&&((1<<(i-1))&x))
ans=max(ans,Dfs(x^(1<<(num-1)),i,j)+A[num][d][i][j]);
return ans;
}
void Work(){
memset(vis,0,sizeof(vis));
int i,j,x,y,cnt=0;
for (i=1;i<=n;i++) T[i].Read();
sort(T+1,T+n+1);
for (i=1;i<=n;i++)
for (j=i+1;j<=n;j++)
if (cont(T[i].s[0],T[j].s[0],T[i].len,T[j].len)||cont(T[i].s[0],T[j].s[1],T[i].len,T[j].len)){
T[i].del=1; cnt++; break;
}
sort(T+1,T+n+1);
n-=cnt;
for (i=1;i<=n;i++)
for (x=0;x<2;x++)
for (j=1;j<=n;j++)
for (y=0;y<2;y++)
A[i][x][j][y]=Count(T[i].s[x],T[j].s[y],T[i].len,T[j].len);//把i串接在j串后
if (n==1){
if (T[1].len-A[1][0][1][0]<2) printf("2\n");//各种答案<2特判
else printf("%d\n",T[1].len-A[1][0][1][0]);//一轮绕了好几圈
return;
}
int ans=0;cnt=0;
for (i=1;i<=n;i++) cnt+=T[i].len;
for (i=2;i<=n;i++)
for (j=0;j<2;j++)
ans=max(ans,Dfs((1<<n)-2,i,j)+A[1][0][i][j]);
if (cnt-ans<2) printf("2\n");
else printf("%d\n",cnt-ans);
}
int main(){
while (1){
scanf("%d",&n);
if (n==0) break;
Work();
}
}