HDU 4628 Pieces(DP + 状态压缩)
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是回文串~这样才算能减一步呀);
代码如下:
1 # include<cstdio> 2 # include<cstring> 3 # include<algorithm> 4 5 using namespace std; 6 7 const int maxn = 16 + 1; 8 const int INF = 0xffffff; 9 char s[maxn]; 10 bool ispal[1<<maxn]; //ispal[i]表示状态i是否是回文,2进制时1表示选择这个字符,0表示不选择这个字符 11 int d[1<<maxn]; //d[S]表示将集合S中的字母删除需要多少步,结果就是d[(1<<n)-1] 12 int n; 13 14 void getPal() //求出所有的回文串 15 { 16 int S, i, j; 17 for(S = 0; S < (1<<n); S++){ //状态S是否回文 18 bool ok = 1; 19 int m = 0, buf[maxn]; //临时存储提取出来的字符 20 for(i = 0; i < n; i++) if((1<<i) & S){ //提取对应字符 21 buf[m++] = s[i]; 22 } 23 for(i = 0, j = m-1; i < j; i++, j--){ //判断回文 24 if(buf[i] != buf[j]){ 25 ok = 0; 26 break; 27 } 28 } 29 ispal[S] = ok; 30 } 31 } 32 33 void dp(){ 34 int S, sub; 35 d[0] = 0; 36 for(S = 1; S < (1<<n); S++){ 37 d[S] = INF; 38 for(sub = S; sub > 0; sub = (sub-1) & S){ //sub = (sub-1) & S)保证是集合S中的字母 39 if(ispal[sub]) d[S] = min(d[S], d[S^sub] + 1); //S^sub就是剩下的字符 40 } 41 } 42 } 43 44 int main() 45 { 46 int T; 47 scanf("%d", &T); 48 while(T--){ 49 scanf("%s", s); 50 n = strlen(s); 51 getPal(); 52 dp(); 53 printf("%d\n", d[(1<<n)-1]); 54 } 55 return 0; 56 }
附贴标程:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 6 int min(int a,int b){ 7 return a<b ?a :b; 8 } 9 10 const int MAX_N = 16, INF = 0xffffff; 11 int n; 12 int dp[1 << MAX_N][MAX_N][MAX_N]; //rest,i,j 13 char s[MAX_N + 1]; 14 void work() { 15 int i,j,k; 16 scanf("%s", s); 17 n = strlen(s); 18 19 for (i = 0; i < n; i++) { 20 for (j = i; j < n; j++) { 21 dp[0][i][j] = 0; 22 } 23 } 24 25 for (int rest = 1; rest < (1 << n); rest++) { 26 for (i = n - 1; i >= 0; i--) { 27 for (j = i; j < n; j++) { 28 //rest,i,j 29 int &ret = dp[rest][i][j] = INF; 30 if (i < j) 31 ret = min(dp[rest][i + 1][j], dp[rest][i][j - 1]); 32 if (s[i] == s[j] && ((rest >> i) & 1) && ((rest >> j) & 1)) { 33 int nrest = rest & (~(1 << i)) & (~(1 << j)); 34 if (nrest == 0) 35 ret = min(ret, dp[nrest][i][j] + 1); 36 else 37 ret = min(ret, dp[nrest][i][j]); 38 } 39 } 40 } 41 for (i = n - 1; i >= 0; i--) { 42 for (int j = i; j < n; j++) { 43 dp[rest][i][j] = min(dp[rest][i][j], dp[rest][0][n - 1] + 1); 44 } 45 } 46 } 47 48 cout << dp[(1 << n) - 1][0][n - 1] << endl; 49 } 50 51 int main() { 52 int T; 53 cin >> T; 54 while (T--) { 55 work(); 56 } 57 }
把每一件简单的事情做好,就是不简单;把每一件平凡的事情做好,就是不平凡!相信自己,创造奇迹~~