1. 程式人生 > 其它 >【YBTOJ】【數位DP】擦除序列

【YBTOJ】【數位DP】擦除序列

擦除序列

給你一個由字母構成的字串 \(S\) 。每一步都要擦除其中一個子序列,但要求被擦除的子序列必須是一個迴文詞。求擦除整個字串的最少步數。
比如,將 \(\texttt{abcba}\)\(\texttt{abyczbea}\) 擦除,就是合理的一步。

\(n\leq16\).

題解

沒想到狀壓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]);
}