题目链接:HDU 1400 Mondriaan's Dream
题目大意:
用\(1\times 2\)的矩形完全覆盖一个\(n\times m\)矩形区域,求覆盖方案数。
题解:
将某一行用二进制来表示是否被覆盖了,压缩信息到一个整数中,进行状态转移。
设\(dp[i][j]\)表示前\(i\)行,除最后一行其他都完全覆盖,而最后一行在\(j\)的二进制对应位为\(1\)的位置竖着覆盖矩形的上半部分,\(0\)的位置横着覆盖矩形或竖着覆盖矩形的下半部分的情况数。
如果\(j\)的二进制为\(0\),若竖着覆盖,则其上一行的对应位置必须为\(1\);若横着覆盖,则其相邻位置必须也有\(0\),设上一行的状态为\(k\),则须满足\(j|k\)中每段连续的\(0\)的个数为偶数(\(j|k\)之后剩下的\(0\)位均为横着覆盖的矩形位置)且\(j\& k=0\)(若某位是\(1\),则表示其是矩形上半部分,则其上一行的对应位置一定是横着覆盖的矩形或者竖着覆盖的矩形的下半部分,肯定为\(0\),所以按位与之后必定为\(0\))。
状态转移方程为:
\[dp[i][j]=\sum_{j,k符合条件}dp[i-1][k] \]初始\(dp[0][0]=1\),答案为\(dp[n][0]\)。
#include <cstring>
#include <iostream>
using namespace std;
long long dp[12][1 << 11];
int n, m;
bool check(int x, int y) {
if (x & y) {
return false;
}
int t = x | y, cnt = 0;
for (int i = 0; i < m; ++i) {
if (!(t & 1 << i)) {
cnt++;
} else if (cnt & 1) {
return false;
}
}
return !(cnt & 1);
}
int main() {
while (cin >> n >> m && (n || m)) {
if (n & 1 && m & 1) {
cout << 0 << endl;
continue;
}
memset(dp, 0, sizeof(dp));
dp[0][0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= (1 << m) - 1; ++j) {
for (int k = 0; k <= (1 << m) - 1; ++k) {
if (check(j, k)) {
dp[i][j] += dp[i - 1][k];
}
}
}
}
cout << dp[n][0] << endl;
}
return 0;
}