zoukankan      html  css  js  c++  java
  • 蒙德里安的梦想(状态压缩DP)

     

     分析: 

      1. 所谓的状态压缩DP,就是用二进制数保存状态。为什么不直接用数组记录呢?因为用一个二进制数记录方便作位运算。

      2. 本题等价于找到所有横放 1 X 2 小方格的方案数,因为所有横放确定了,那么竖放方案是唯一的。

      3. 用f[i][j]记录第i-1列已经充满且第i列第j个状态。j状态位等于1表示上一列有横放格子,本列有格子捅出来。转移方程很简单,本列的每一个状态都由上列所有“合法”状态转移过来f[i][j] += f[i - 1][k]

      4. 两个转移条件: i 列和 i - 1列同一行不同时捅出来 ; 本列捅出来的状态j和上列捅出来的状态k求或,得到上列是否为奇数空行状态,奇数空行不转移。

      5. 初始化条件f[0][0] = 1,第0列只能是状态0,无任何格子捅出来。返回f[m][0]。第m + 1列不能有东西捅出来。

    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int N = 12, M = 1 << N;
    
    bool st[M];
    
    long long f[N][M];
    
    int main() {
        int m, n;
        while(cin >> m >> n && (m || n)) {
            
            for(int i = 0; i < 1 << m; i++) {
                int count = 0;
                st[i] = true;
                for(int j = 0; j < m; j++) {
                    if(!(i & (1 << j))) {
                        count++;
                    } else {
                        if(count & 1) {
                            st[i] = false;
                        }
                        count = 0;
                    }
                }
                if(count & 1) st[i] = false;
            }
            memset(f,0,sizeof f);
            f[0][0] = 1;
            
            for(int i = 1; i <= n; i++) {
                for(int j = 0; j < 1 << m; j++) {
                    for(int k = 0; k < 1 << m; k++) {
                        if((j & k) == 0 && st[j | k]) { //st[j | k] : 表示从k转移到j 的状态时 i-1列是合法的(i-1列可以被完全填充)
                            f[i][j] += f[i-1][k];
                        }
                    }
                }
            }
            cout << f[n][0] << endl;
        }
        return 0;
    }
  • 相关阅读:
    c++ struct 使用
    c++数组、字符串操作
    c++ List、Vector、Stack、Queue使用
    十三、哈希表
    十二、234树
    十一、红黑树
    十、哈夫曼编码
    九、二叉树
    八、高级排序
    七、递归
  • 原文地址:https://www.cnblogs.com/yonezu/p/13408531.html
Copyright © 2011-2022 走看看