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
", 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 }
原文地址:https://www.cnblogs.com/acm-bingzi/p/3231780.html