蒙德里安的梦想
思路 状态压缩DP
状态表示: 设\(f[i][j]\)表示前\(i-1\)列已经摆放整齐, 第\(i\)列的状态为\(j\)时的方案数,\(j\)用十进制来表示二进制数,当\(j\)的第\(k\)位为\(0\)时,表示该列没有方块覆盖,即可以理解为该列没有被\(i - 1\)列的\(1 \times 2\)方格覆盖。
集合划分: 第\(i - 1\)列中的\(1 \times 2\)方格摆放已经由状态\(j\)确定, 那么对于\(f[i - 1][k]\), 需要满足\(k \& j = 0\), 对于\(f[i - 1][k]\) 由于前\(i - 2\)列的方格已经摆放整齐,并且第\(i - 2\)列中\(1 \times 2\)方格已经由\(k\)确定, 那么对于未填充的方格,我们只能用\(2 \times 1\)方格来填充, 通过这样的操作,我们便可以从\(f[i - 1][k]\) 转移到\(f[i][j]\)。
状态计算:
\[S = \left \{j \& k = 0 \space and \space even(k) \right \} \\
f[i][j] = \sum _ {k \in S} f[i - 1][k] \\
\]
\(even(k)\) 表示\(k\)没有奇数个连续的\(0\)
Code
#include <iostream>
#include <cstring>
#include <vector>
#define sz(x) (int) x.size()
#define debug(x) std::cout << "debug:" << x << "\n";
using i64 = long long;
const int N = 20, M = 1 << 11;
i64 f[N][M];
std::vector<int> state[M];
bool st[M];
int main() {
int n, m;
while(std::cin >> n >> m, n || m) {
for(int i = 0; i < 1 << n; i ++) {
int cnt = 0;
bool ok = true;
for(int j = 0; j < n; j ++) {
if(i >> j & 1) {
if(cnt & 1) {
ok = false;
break;
}
cnt = 0;
} else cnt ++;
}
if(cnt & 1) ok = false;
st[i] = ok;
}
for(int i = 0; i < 1 << n; i ++) {
state[i].clear();
for(int j = 0; j < 1 << n; j ++) {
if((i & j) == 0 && st[i | j]) {
state[i].push_back(j);
}
}
}
memset(f, 0, sizeof f);
f[0][0] = 1;
for(int i = 1; i <= m; i ++) {
for(int j = 0; j < 1 << n; j ++) {
for(int k = 0; k < sz(state[j]); k ++) {
f[i][j] += f[i - 1][state[j][k]];
}
}
}
std::cout << f[m][0] << "\n";
}
}