【状压dp&预处理难】Fun Game UVA1204

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


猜你喜欢

转载自blog.csdn.net/lerbon23james/article/details/80058127