AcWing 291. 蒙德里安的梦想

AcWing 291. 蒙德里安的梦想

题意:给出 \(n<=11, m<=11\)的矩阵,要求将矩阵全部恰好分成 \(1\times 2\) 或者 \(2\times 1\) 的小矩阵,问方案数。
题解:主要是dp数组的含义定义要了解,设 \(dp_{i, j}\) 表示的是第 \(i\)\(j\) 形状的方案数,这里的 \(j\) 代表的是二进制的状态。想象一个 \(01\) 串,然后在串中第 \(x\) 个0代表的是此列的第 \(x\) 行这个位置,是横着的,且是横着的第一个格子。是第一个格子,所以在 \(i\) 列,考虑 \(i - 1\) 列时,\(j_{i-1}\) 才可以与 \(j_{i}\) 进行 \(|\) 运算,然后 \(1\)\(1\) 之间的 \(0\) 的数量必须是偶数才合法。注意会爆 \(long long\).
代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <map>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 12;
ll dp[N][(1 << N)];
bool vis[N];
bool check(int x, int n) {
    int cnt = 0;
    int  d =0 ;
    while (x) {
        if (x & 1) {
            if (cnt % 2 != 0)return 0;
            cnt = 0;
        }
        else {
            cnt++;
        }
        d++;
        x /= 2;
       
    }
    if ((n - d + cnt) % 2 != 0)return 0;
    return 1;
}
void out(int x) {
    if (x == 0) cout << 0;
    while (x) {
        if (x & 1)cout << 1;
        else cout << 0;
        x /= 2;
    }
    return;
}
void solve() {
    int n, m;
    while (cin >> n >> m && n && m) {
        memset(dp, 0, sizeof dp);
        dp[0][0] = 1;
        int ans = 0;
        for (int i = 1; i <= m; i++) {
            for (int j = 0; j < 1 << n; j++) {
                for (int k = 0; k < 1 << n  ; k++) {
                    if ( (k & j) == 0  && check(j | k, n)) {//(k & j == 0)检查是否横格有冲突,check( j | k, n)检查是否竖格合法。
                        dp[i][j] += dp[i-1][k];
                    }
                }
               
            }
        }
        cout << dp[m][0] << endl;
    }
   
}
int main() {
    int t = 1;//cin >> t;
    while (t--) solve();
    return 0;
}
posted @ 2021-03-08 18:48  u_yan  阅读(63)  评论(0编辑  收藏  举报