[hdu4628 Pieces]二进制子状态,DP

题意:给一个长度为16的字符串,每次从里面删掉一个回文序列,求最少需要几次才能删掉所有字符

思路:二进制表示每个字符的状态,那么从1个状态到另一个状态有两种转移方式,一是枚举所有合法的回文子序列,判断是否是当前状态的子状态,再转移,二是枚举当前状态的所有子状态来转移。前者最坏复杂度O(2^16*2^16) = O(几十亿),而后者最坏只有(i:1->16)Σ2iC(16,i) = O(几千万)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <deque>
#include <queue>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

#define X                   first
#define Y                   second
#define pb                  push_back
#define mp                  make_pair
#define all(a)              (a).begin(), (a).end()
#define fillchar(a, x)      memset(a, x, sizeof(a))
#define copy(a, b)          memcpy(a, b, sizeof(a))

typedef long long ll;
typedef pair<int, int> pii;
typedef unsigned long long ull;

#ifndef ONLINE_JUDGE
void RI(vector<int>&a,int n){a.resize(n);for(int i=0;i<n;i++)scanf("%d",&a[i]);}
void RI(){}void RI(int&X){scanf("%d",&X);}template<typename...R>
void RI(int&f,R&...r){RI(f);RI(r...);}void RI(int*p,int*q){int d=p<q?1:-1;
while(p!=q){scanf("%d",p);p+=d;}}void print(){cout<<endl;}template<typename T>
void print(const T t){cout<<t<<endl;}template<typename F,typename...R>
void print(const F f,const R...r){cout<<f<<", ";print(r...);}template<typename T>
void print(T*p, T*q){int d=p<q?1:-1;while(p!=q){cout<<*p<<", ";p+=d;}cout<<endl;}
#endif
template<typename T>bool umax(T&a, const T&b){return b<=a?false:(a=b,true);}
template<typename T>bool umin(T&a, const T&b){return b>=a?false:(a=b,true);}
template<typename T>
void V2A(T a[],const vector<T>&b){for(int i=0;i<b.size();i++)a[i]=b[i];}
template<typename T>
void A2V(vector<T>&a,const T b[]){for(int i=0;i<a.size();i++)a[i]=b[i];}

const double PI = acos(-1.0);
const int INF = 1e9 + 7;

/* -------------------------------------------------------------------------------- */

char s[20];
int n, dp[1 << 20];

void init() {
    for (int i = 0; i < (1 << n); i ++) dp[i] = INF;
    dp[0] = 1;
    for (int i = 1; i < (1 << n); i ++) {
        int j, k;
        for (j = n - 1; j >= 0; j --) {
            if (i & (1 << j)) break;
        }
        for (k = 0; k < n; k ++) {
            if (i & (1 << k)) break;
        }
        if (s[j] == s[k] && dp[i ^ (1 << j) ^ (1 << k)] < INF)
            dp[i] = dp[i ^ (1 << j) ^ (1 << k)];
        if (j == k) dp[i] = 1;
    }
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
#endif // ONLINE_JUDGE
    int T;
    cin >> T;
    while (T --) {
        scanf("%s", s);
        n = strlen(s);
        init();
        for (int i = 1; i < (1 << n); i ++) {
            for (int j = i; j; j = (j - 1) & i) {
                umin(dp[i], dp[i ^ j] + dp[j]);
            }
        }
        cout << dp[(1 << n) - 1] << endl;
    }
    return 0;
}
时间: 2024-08-02 03:03:39

[hdu4628 Pieces]二进制子状态,DP的相关文章

hdu 1208 Pascal&#39;s Travels (子状态继承dp)

Pascal's Travels Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 1774    Accepted Submission(s): 781 Problem Description An n x n game board is populated with integers, one nonnegative integer

poj3254二进制放牛——状态压缩DP

题目:http://poj.org/problem?id=3254 利用二进制压缩状态,每一个整数代表一行的01情况: 注意预处理出二进制表示下没有两个1相邻的数的方法,我的方法(不知为何)错了,看到了别人的优美方法: 再进行DP即可. 代码如下: #include<iostream> #include<cstdio> using namespace std; int m,n,a[15],num[15],p[3005],f[15][3005],tot,INF=100000000,a

点集配对问题(状态dp)

给定n个点(n是偶数)使得两个点两两配对,最后总的距离和最小. 用是表示集合,那么dp[s]表示集合s配对后的最小距离和  , 状态转换方程为  表示集合中任意拿两个元素配对,然后转移为更小的两个集合的点集配对.i=min(s)表示i为集合中的第一个元素,因为第一个元素肯定要配对的, 所以找到集合中的第一个元素后,我们枚举要配对的另外一个元素j,j>i && j属于s 我们用二进制表示一个集合,1表示该元素存在,0表示不存在,然后进行状态转移 我们不需要担心一个状态的子状态没有被算出

HDU-4628 Pieces 如压力DP

鉴于他的字符串,每一个都能够删除回文子串.子可以是不连续,因此,像更好的模拟压力.求删除整个字符串需要的步骤的最小数量. 最大长度为16,因此不能逐行枚举状态.首先预处理出来全部的的回文子串,然后从第一步開始,依次状压第i步能到达的状态.假设能达到母串,跳出. 还有初始化不要用图省事用memset. . 不优越的姿势+函数导致T了数发. #include <iostream> #include <cstdio> #include <cstring> #include &

HDU-4628 Pieces 状压DP

给出一行字符串,每次可以删去一个回文子串,子串可以是不连续的,因此用状压比较好模拟,求删掉整个字符串需要的最少步数. 字符串的最大长度为16,因此不能逐行枚举状态,首先预处理出来所有的的回文子串,然后从第一步开始,依次状压第i步能到达的状态,如果能达到母串,跳出. 还有初始化不要用图省事用memset..不优越的姿势+函数导致T了数发. #include <iostream> #include <cstdio> #include <cstring> #include &

ZOJ-3777-Problem Arrangement(状态DP)

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3777 题意: 输入n和m,接下来一个n*n的矩阵,a[i][j]表示第i道题放在第j个顺序做可以加a[i][j]的分数,问做完n道题所得分数大于等于m的概率.用分数表示,分母为上述满足题意的方案数,分子是总的方案数,输出最简形式. 分析: 一开始用的DFS做的,复杂度太高,果断超时.. 正解是状压DP: 由于总的方案数为n! ,简化为求给一个n*n的矩阵,每一行每一列各选

hdu4800_Josephina and RPG(二维状态dp)

/////////////////////////////////////////////////////////////////////////////////////////////////////// 作者:tt2767 声明:本文遵循以下协议自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0 查看本文更新与讨论请点击:http://blog.csdn.net/tt2767 链接被删请百度: CSDN tt2767 ///////////////

Missile:双状态DP

题目 描写叙述 Long , long ago ,country A invented a missile system to destroy the missiles from their enemy . That system can launch only one missile to destroy multiple missiles if the heights of all the missiles form a non-decrease sequence . But recentl

ZOJ 3802 Easy 2048 Again 状态DP

zoj 上次的月赛题,相当牛的题目啊,根本想不到是状态压缩好吧 有个预先要知道的,即500个16相加那也是不会超过8192,即,合并最多合并到4096,只有2的12次方 所以用状态压缩表示前面有的序列组合,找到了符合的,就往上累加合并生成新状态,否则就添加到前面的状态的后面构成新状态,因为每一个的状态都由前一个所得,用滚动数组即可 #include <iostream> #include <cstdio> #include <cstring> #include <