• 状态压缩的基本操作


    数量规模不超过20时 要存储每个状态取或不取的状态时可以用状态压缩。

    所谓状态压缩,就是用二进制数表示集合 用1代表有该元素,0代表没有该元素

    {100101}=37  表示集合中有第1,3,6个元素。  用状压dp存储状态消耗的空间是2^n

    例题1:

    有一个n*m的矩阵 其中有的格子可以选,有的不能选, 且不能选中相邻的格子

    问: 最多能选定多少个格子?

    例如下面的格子, 可选的标为1,不可选的标为0

    选择的方案是  共3个可选位置

    解决方案: 自上而下逐行的选择格子,每行的格子选择限定条件:1 这行原本的限制可选位置、2 这行选的格子不能相邻、3 这行的格子不能和上一行的格子相邻

     这一行的选择情况可以用二进制表示,设这行选了now , 这行的限定条件是 lim , 上一行选了 pre 

    对于条件1: lim | now == lim

      条件2: now&(now>>1) ==0

      条件3: now&pre==0

    完整代码

     1 const int MAX_N = 20;
     2 const int MAX_M = 20;
     3 int state[MAX_N + 1];
     4 int dp[MAX_N + 1][1 << MAX_M];
     5 
     6 bool not_intersect(int now, int prev) {
     7     return (now & prev) == 0;
     8 }
     9 
    10 bool fit(int now, int flag) {
    11     return (now | flag) == flag;
    12 }
    13 bool ok(int x) {
    14     // 行内自己是否相交
    15     return (x & (x / 2)) == 0;
    16 }
    17 int count(int now) {
    18     int s = 0;  // 统计 now 的二进制形式中有多少个 1
    19     while (now) {
    20         s += (now & 1);  // 判断 now 二进制的最后一位是否为 1,如果是则累加
    21         now >>= 1;  // now 右移一位
    22     }
    23     return s;
    24 }
    25 
    26 int main() {
    27     int n, m;
    28     cin >> n >> m;
    29     // 初始化所有数组
    30     for (int i = 1; i <= n; ++i) {
    31         for (int j = 0; j < m; ++j) {
    32             int flag;
    33             cin >> flag;
    34             state[i] |= (1 << j) * flag;  // 将 (i,j) 格子的状态放入 state[i] 中,state[i] 表示第 i 行的可选格子组成的集合
    35         }
    36     }
    37     for (int i = 1; i <= n; ++i) {
    38         for (int j = 0; j < (1 << m); ++j) {  // 枚举当前行的状态
    39             if (!ok(j) || !fit(j, state[i])) {  // 如果当前行状态不合法则不执行后面的枚举
    40                 continue;
    41             }
    42             int cnt = count(j);  // 统计当前行一共选了多少个格子
    43             for (int k = 0; k < (1 << m); ++k) {
    44                 if (ok(k) && fit(k, state[i - 1]) && not_intersect(j, k)) {  // 判断前一行是否合法和当前行和前一行的方案是否冲突
    45                     dp[i][j] = max(dp[i][j], dp[i - 1][k] + cnt);  // 更新当前行、当前状态的最优解
    46                 }
    47             }
    48         }
    49     }
    50     int ans = 0;  // 保存最终答案
    51     for (int i = 0; i < (1 << m); ++i) {
    52         ans = max(ans, dp[n][i]);  // 枚举所有状态,更新最大值
    53     }
    54     cout << ans << endl;
    55     return 0;
    56 }
    View Code

    统计二进制数中1的数目

    int count(int x)
    {
        int num=0;
        while(x)
        {
           num+=x&1;
            x>>=1;
        }
        return num;
    }

    例子2:  消除回文串

    给定一个字符串s  可以从中消除回文串, 回文串不必连续,问最少多少次可以消除整个串

    状态d[ i ] 表示消除字符串i 需要的最少步数。

    状态转移: if( i串不是回文串)  d[ i ] = d [ j ] + d [ j^i];  else  d[ i ] = 1;   // j  是i串的子集 ,  j^i 是  j的补集

    for (int t = 1; t < (1 << n); t++) {  // 枚举当前状态
         if(IsPalindrome(t) ) dp[t]=1 ;  // 判断当前状态是否是回文,如果是回文则步骤数为 1
        else {
            for(int i = t; i; i = (i - 1) & t) { // 枚举 t 的所有子集
                dp[t] = min(dp[t], dp[i] + dp[t ^ i]);  // 更新当前状态的解的最小值
            }
        }
    }
    printf("%d
    ", dp[(1 << n) - 1]);  // 输出最终答案

    //枚举子集  for(int i = t ; i ; i = (i - 1) & t ) 

    落霞与孤鹜齐飞,秋水共长天一色
  • 相关阅读:
    Windows光标形状
    函数对象(仿函数 functor)
    构造函数的初始化列表抛出异常
    <<Windows via C/C++>>学习笔记 —— 线程优先级【转】
    单例模式
    c++中的重载(Overload)、覆盖(重写,Override) 、隐藏与访问权限控制及using声明
    RTTI: dynamic_cast typeid
    抽象类 虚函数 声明与实现
    typedef 函数指针 数组 std::function
    Client Window坐标 RECT相关函数
  • 原文地址:https://www.cnblogs.com/star-and-me/p/7683980.html
Copyright © 2020-2023  润新知