题意:
一群小孩(至少两个)围成一圈做游戏。每一轮从某个小孩开始往他左边或者右边传手帕。一个小孩拿到手帕后在手帕上写上自己的性别,男孩写B,女孩写G,然后按照相同的方向传递给下一个小孩,每一轮都可能在任何一个小孩写完后停止,现在游戏已经进行了n轮,已知n轮中每轮手帕上留下的字,问最少可能有几个小孩。
分析:
定义状态dp[i][j][x]表示当前拿的字符串状态为i,最后一次拿的字符串为j,j的正反状态为x的最少长度。
预处理去掉所有的被其他串包含的串,然后处理出所有串的重叠长度。
状态的转移就很好写了嘛,考虑当前一个没有拿到的串,假设为j,正反状态为y(j要求没有拿过,即i&(1<<j)为0),当前拿到的串的集合为s且当前最后一次的串为i,i的状态是x
dp[s|(1<<j)][j][y]=dp[s][i][x]+length[j]-overlap[i][j][x][y]
length[j]代表第j个字符串的长度,overlap[i][j][x][y]代表正反状态为x的第i字符串和正反状态为y的第j字符串的重叠长度(i左j右)
#include<bits/stdc++.h>
using namespace std;
const int maxn=1<<17;
int dp[maxn+10][20][2]; //dp[i][j][x]表示当前拿的字符串状态为i(状态压缩的集合),最后一次拿的字符串为j,j的正反状态为x的最少长度。
int n; //
int over[20][20][2][2]; //overlap[i][j][x][y]代表正反状态为x的第i字符串和正反状态为y的第j字符串的重叠长度(i左j右)
int len[20]; //length[j]代表第j个字符串的长度
string arr[20][2];
struct Node{
string str,rstr;
bool operator < (const Node & t) const {
return str.size()<t.str.size();
}
};
inline void update(int &x,int v){
if(x<0||x>v) x=v;
}
void solve(){
memset(dp,-1,sizeof dp);
int limi=(1<<n)-1;
dp[1][0][0]=len[0];
for(int s=1;s<limi;++s)
for(int i=0;i<n;++i)
for(int x=0;x<2;++x)
if(dp[s][i][x]>=0)
for(int k=1;k<n;++k)
if(!(s&1<<k))
for(int y=0;y<2;++y)
update(dp[s|(1<<k)][k][y],dp[s][i][x]+len[k]-over[i][k][x][y]);
int ans=-1;
for(int i=0;i<n;++i)
for(int x=0;x<2;++x)
if(dp[limi][i][x]>=0)
update(ans,dp[limi][i][x]-over[i][0][x][0]);
if(ans<=1) ans=2;
cout<<ans<<endl;
}
int overlap(const string& a,const string& b){
int n1=a.size(),n2=b.size();
bool flag=true;
for(int i=1;i<n1;++i,flag=true)
if(n2+i>n1){
for(int j=0;j+i<n1;++j)
if(a[i+j]!=b[j]){
flag=false;
break;
}
if(flag)
return n1-i;
}
return 0;
}
void init(){
int n2=0;
Node node[20];
for(int i=0;i<n;i++){
cin>>node[i].str,node[i].rstr=node[i].str;
reverse(node[i].rstr.begin(),node[i].rstr.end());
}
sort(node,node+n);
for(int i=0,j;i<n;i++){
for(j=i+1;j<n;j++){
if(node[j].str.find(node[i].str)!=string::npos||node[j].str.find(node[i].rstr)!=string::npos)
break;
}
if(j==n){
arr[n2][0]=node[i].str;
arr[n2][1]=node[i].rstr;
len[n2++]=node[i].str.size();
}
}
n=n2;
for(int i=0;i<n;++i)
for(int j=0;j<n;++j)
for(int k=0;k<2;++k)
for(int l=0;l<2;++l)
over[i][j][k][l]=overlap(arr[i][k],arr[j][l]);
}
int main(){
while(cin>>n,n){
init();
solve();
}
return 0;
}