AcWing 291. 蒙德里安的梦想
题意:给出 (n<=11, m<=11)的矩阵,要求将矩阵全部恰好分成 (1 imes 2) 或者 (2 imes 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;
}