AcWing 291. 蒙德里安的梦想 状态压缩

#include<bits/stdc++.h>
using namespace std;
const int N = 12, M = 1 << N;
int st[M];
long long f[N][M];
//用f[i][j]记录第i列第j个状态。j状态位等于1表示上一列有横放格子,本列有格子捅出来
//j是一个二进制数字     如果第1个位数字位1,表示第i-1列的第1行位一个横着放的格子的左端点
int main() {
    int n, m;
    while (cin >> n >> m && (n || m)) {
        //先预处理,所有状态是否不存在连续奇数个0
        for (int i = 0; i < 1 << n; i ++) { //所有的状态是1左移n位
            st[i] = true;//假设是成立的
            int cnt = 0;//当前连续0的个数
            for (int j = 0; j < n; j ++)
                if (i >> j & 1) {//如果说当前这一位是1,说明上面连续0的那一段已经结束
                    if (cnt & 1) //判断上一段连续0是否为奇数个
                        st[i] = false;
                    cnt = 0;//情空
                } else cnt ++;
            if (cnt & 1) st[i] = false; // 扫完后要判断一下最后一段有多少个连续的0
        }
        memset(f, 0, sizeof f);
        //最一开始的时候,小方块什么都没有放
        f[0][0] = 1;//第一列只能0,上一列不存在,只有这一种方法
        for (int i = 1; i <= m; i ++)//枚举所有列
            for (int j = 0; j < 1 << n; j ++)//枚举所有状态
                for (int k = 0; k < 1 << n; k ++)//枚举第i-1列所有的状态
                    if ((j & k) == 0 && (st[j | k]))
                        // (j & k) == 0 表示 i 列和 i - 1列同一行不同时捅出来
                        // st[j | k] == 1 表示 在 i 列状态 j, i - 1 列状态 k 的情况下,i - 1 列无连续空行.
                        f[i][j] += f[i - 1][k];
        cout << f[m][0] << endl;//m -1列不能有横向格子的起点,所有都为0
    }
    return 0;
}
原文地址:https://www.cnblogs.com/QingyuYYYYY/p/11906299.html