Pieces
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4628
题目大意:给定一个字符串s,如果子序列中有回文,可以一步删除掉它,求把整个序列删除所需要的最少步数。比如: axbyczbea 可以一次删除掉 abcba 得到 xyze
Sample Input
2
aa
abb
Sample Output
1
2
分析:这道题目刚出来时居然有超过一半的人AC,是我太弱了吗?
到底不会,先贴出标程,再慢慢消化好了
集合上的动态规划。。。和点集配对很像,这里我先求出所有的回文串,然后dp。
设d[S]表示将集合S中的字母删除需要多少步,结果就是d[(1<<n)-1];
枚举所有的S,枚举所有S的子集sub;
状态转移方程:d[S] = min(d[S], d[S^sub)] + 1](如果sub是回文串~这样才算能减一步呀);
代码如下:
# include<cstdio>
# include<cstring>
# include<algorithm> using namespace std; const int maxn = + ;
const int INF = 0xffffff;
char s[maxn];
bool ispal[<<maxn]; //ispal[i]表示状态i是否是回文,2进制时1表示选择这个字符,0表示不选择这个字符
int d[<<maxn]; //d[S]表示将集合S中的字母删除需要多少步,结果就是d[(1<<n)-1]
int n; void getPal() //求出所有的回文串
{
int S, i, j;
for(S = ; S < (<<n); S++){ //状态S是否回文
bool ok = ;
int m = , buf[maxn]; //临时存储提取出来的字符
for(i = ; i < n; i++) if((<<i) & S){ //提取对应字符
buf[m++] = s[i];
}
for(i = , j = m-; i < j; i++, j--){ //判断回文
if(buf[i] != buf[j]){
ok = ;
break;
}
}
ispal[S] = ok;
}
} void dp(){
int S, sub;
d[] = ;
for(S = ; S < (<<n); S++){
d[S] = INF;
for(sub = S; sub > ; sub = (sub-) & S){ //sub = (sub-1) & S)保证是集合S中的字母
if(ispal[sub]) d[S] = min(d[S], d[S^sub] + ); //S^sub就是剩下的字符
}
}
} int main()
{
int T;
scanf("%d", &T);
while(T--){
scanf("%s", s);
n = strlen(s);
getPal();
dp();
printf("%d\n", d[(<<n)-]);
}
return ;
}
附贴标程:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std; int min(int a,int b){
return a<b ?a :b;
} const int MAX_N = , INF = 0xffffff;
int n;
int dp[ << MAX_N][MAX_N][MAX_N]; //rest,i,j
char s[MAX_N + ];
void work() {
int i,j,k;
scanf("%s", s);
n = strlen(s); for (i = ; i < n; i++) {
for (j = i; j < n; j++) {
dp[][i][j] = ;
}
} for (int rest = ; rest < ( << n); rest++) {
for (i = n - ; i >= ; i--) {
for (j = i; j < n; j++) {
//rest,i,j
int &ret = dp[rest][i][j] = INF;
if (i < j)
ret = min(dp[rest][i + ][j], dp[rest][i][j - ]);
if (s[i] == s[j] && ((rest >> i) & ) && ((rest >> j) & )) {
int nrest = rest & (~( << i)) & (~( << j));
if (nrest == )
ret = min(ret, dp[nrest][i][j] + );
else
ret = min(ret, dp[nrest][i][j]);
}
}
}
for (i = n - ; i >= ; i--) {
for (int j = i; j < n; j++) {
dp[rest][i][j] = min(dp[rest][i][j], dp[rest][][n - ] + );
}
}
} cout << dp[( << n) - ][][n - ] << endl;
} int main() {
int T;
cin >> T;
while (T--) {
work();
}
}