题意:一群小孩(至少两个)围成一圈做游戏。每一轮从某个小孩开始往他左边或者右边传手帕。一个小孩拿到手帕后在手帕上写上自己的性别,男孩写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(); } }