分析:
1. 所谓的状态压缩DP,就是用二进制数保存状态。为什么不直接用数组记录呢?因为用一个二进制数记录方便作位运算。
2. 本题等价于找到所有横放 1 X 2 小方格的方案数,因为所有横放确定了,那么竖放方案是唯一的。
3. 用f[i][j]记录第i-1列已经充满且第i列第j个状态。j状态位等于1表示上一列有横放格子,本列有格子捅出来。转移方程很简单,本列的每一个状态都由上列所有“合法”状态转移过来f[i][j] += f[i - 1][k]
4. 两个转移条件: i 列和 i - 1列同一行不同时捅出来 ; 本列捅出来的状态j和上列捅出来的状态k求或,得到上列是否为奇数空行状态,奇数空行不转移。
5. 初始化条件f[0][0] = 1,第0列只能是状态0,无任何格子捅出来。返回f[m][0]。第m + 1列不能有东西捅出来。
#include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 12, M = 1 << N; bool st[M]; long long f[N][M]; int main() { int m, n; while(cin >> m >> n && (m || n)) { for(int i = 0; i < 1 << m; i++) { int count = 0; st[i] = true; for(int j = 0; j < m; j++) { if(!(i & (1 << j))) { count++; } else { if(count & 1) { st[i] = false; } count = 0; } } if(count & 1) st[i] = false; } memset(f,0,sizeof f); f[0][0] = 1; for(int i = 1; i <= n; i++) { for(int j = 0; j < 1 << m; j++) { for(int k = 0; k < 1 << m; k++) { if((j & k) == 0 && st[j | k]) { //st[j | k] : 表示从k转移到j 的状态时 i-1列是合法的(i-1列可以被完全填充) f[i][j] += f[i-1][k]; } } } } cout << f[n][0] << endl; } return 0; }