擦除序列
给你一个由字母构成的字符串 (S) 。每一步都要擦除其中一个子序列,但要求被擦除的子序列必须是一个回文词。求擦除整个字符串的最少步数。
比如,将 ( exttt{abcba}) 从 ( exttt{abyczbea}) 擦除,就是合理的一步。(nleq16).
题解
没想到状压DP还可以这么用……
设状态表示每一位是否被擦去,对于每一个状态,枚举其子集并判断子集是不是回文串。
(但是因为智障错误调了快一个点……)
代码
#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
const int INF = 0x3f3f3f3f,N = 20,M = 1e5+5;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
ll ret=0;char ch=' ',c=getchar();
while(!(c>='0'&&c<='9'))ch=c,c=getchar();
while(c>='0'&&c<='9')ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
return ch=='-'?-ret:ret;
}
int n;
char a[N];
ll dp[M];
bool flg[M];
signed main(){
memset(dp,0x3f,sizeof(dp));
scanf("%s",a+1);
n = strlen(a+1);
dp[0] = 0;
for(int i = 1 ; i < 1<<n ; i ++){
char ch[N]; int cnt = 0;
for(int j = 1 ; j <= n ; j ++)
if(i & (1<<(j-1)))
ch[++cnt] = a[j];
flg[i] = 1;
for(int j = 1 ; j <= (cnt+1)>>1 ; j ++)
if(ch[j] != ch[cnt-j+1]) {flg[i] = 0;break;}
}
for(int i = 1 ; i < 1<<n ; i ++)
for(int j = i ; j ; j = (j-1) & i)
if(flg[j])
dp[i] = min(dp[i],dp[i^j] + 1);
printf("%lld",dp[(1<<n)-1]);
}