求把 N×MN×M 的棋盘分割成若干个 1×21×2 的的长方形,有多少种方案。
例如当 N=2,M=4N=2,M=4 时,共有 55 种方案。当 N=2,M=3N=2,M=3 时,共有 33 种方案。
如下图所示:
输入格式
输入包含多组测试用例。
每组测试用例占一行,包含两个整数 NN 和 MM。
当输入用例 N=0,M=0N=0,M=0 时,表示输入终止,且该用例无需处理。
输出格式
数据范围
输入样例:
1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0
输出样例:
1 0 1 2 3 5 144 51205
//f[i,j] 表示已经将前i-1列摆好,且从i-1列伸到i列的状态是j的所有方案
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
typedef long long LL;
using namespace std;
const int N = 12, M = 1 << N; //M:列的状态
LL f[N][M];
bool st[M];
vector<int> state[M];
int n,m;
int main(){
while (cin >> n >> m, n || m) {
//判断列是否能塞满小方格(偶数个连续的空格)
for (int i = 0; i < 1 << n; ++i) {
int cnt = 0; //记录0个数
bool isValid = true; //判断是否合法
for (int j = 0; j < n; ++j)
if (i >> j & 1) {
if (cnt & 1) { //前面0个数为奇数
isValid = false;
break;
}
cnt = 0; //把cnt置为0
}else cnt++;
if (cnt & 1) isValid = false;
st[i] = isValid;
}
//处理是否能从j状态转换为i状态
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);
}
}
//f[0,0] 也是一种状态从,因为不存在-1列伸过来
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 (auto & k : state[j]) {
f[i][j] += f[i-1][k];
}
}
}
cout << f[m][0] << endl;
}
return 0;
}