题目
2013 暑假多校训练 3
题意:
一个字符串,每次可以删掉一个子序列,且这个子序列必须构成回文串,求全部删完的最少次数。
题解:
枚举要删除的子序列,剩下的继续删除,记下每个部分的最少删除次数。
代码写得很挫……
//Time:125ms //Memory:560KB #include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <map> #define MAXN 20 #define MAXM (1<<16) #define FI first #define SE second #define INF 1000000007 #define MP(x,y) make_pair((x),(y)) using namespace std; struct _node { char str[MAXN]; int pos[MAXN]; int len; }; int pre[MAXM]; int dp[MAXN][MAXN]; pair<int,int> fa[MAXN][MAXN]; void cal(char *s,int len,int x,int y) { for(int i=x;i>=0;--i) for(int j=y;j<len;++j) if(i!=x||j!=y) { dp[i][j]=0; if(i<x&&dp[i+1][j]>dp[i][j]) dp[i][j]=dp[i+1][j],fa[i][j]=MP(i+1,j); if(j>y&&dp[i][j-1]>dp[i][j]) dp[i][j]=dp[i][j-1],fa[i][j]=MP(i,j-1); if(i<x&&j>y&&s[i]==s[j]) if(dp[i+1][j-1]+2>dp[i][j]) dp[i][j]=dp[i+1][j-1]+2,fa[i][j]=MP(0,-1); } } int solve(_node no) { _node tno; int ans=INF,tmp; pair<int,int> now; bool vi[MAXN]={0}; for(int i=0;i<no.len;++i) { for(int j=i;j<no.len;++j) { if(no.str[j]!=no.str[i]) continue; memset(dp,0,sizeof(dp)); memset(vi,0,sizeof(vi)); dp[i][j]=(i!=j?2:1); fa[i][j]=MP(-1,-1); cal(no.str,no.len,i,i); now=MP(0,no.len-1); vi[i]=vi[j]=1; while(now.FI!=-1) { if(fa[now.FI][now.SE]==MP(0,-1)) vi[now.FI]=1,vi[now.SE]=1,++now.FI,--now.SE; else now=fa[now.FI][now.SE]; } tmp=0; tno.len=0; for(int i=0;i<no.len;++i) if(!vi[i]) { tno.str[tno.len]=no.str[i]; tno.pos[tno.len++]=no.pos[i]; tmp|=1<<no.pos[i]; } if(pre[tmp]<INF) ans=min(ans,1+pre[tmp]); else ans=min(ans,1+solve(tno)); } } tmp=0; for(int i=0;i<no.len;++i) tmp|=1<<(no.pos[i]); pre[tmp]=min(pre[tmp],ans); return ans; } int main() { //freopen("/home/moor/Code/input","r",stdin); int ncase; _node no; scanf("%d",&ncase); while(ncase--) { scanf("%s",no.str); memset(pre,0x3f,sizeof(pre)); pre[0]=0; no.len=strlen(no.str); for(int i=0;i<no.len;++i) no.pos[i]=i; printf("%d\n",solve(no)); } return 0; }