AcWing-291. 蒙德里安的梦想
原题地址
题目描述
求把N*M的棋盘分割成若干个1*2的的长方形,有多少种方案。
例如当N=2,M=4时,共有5种方案。当N=2,M=3时,共有3种方案。
如下图所示:

输入格式
输入包含多组测试用例。
每组测试用例占一行,包含两个整数N和M。
当输入用例N=0,M=0时,表示输入终止,且该用例无需处理。
输出格式
每个测试用例输出一个结果,每个结果占一行。
数据范围
1≤N,M≤11
输入样例
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
算法1
状压DP,时间复杂度:\(O(n * 2 ^ {2M})\)
C++ 代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 15, M = 1 << 11;
//当确定所有竖直长方形的位置后,水平长方形摆放的位置是固定的,所以总方案个数等于摆放所有竖直长方形的个数。
int n, m;
bool st[M]; // 在某一行摆放了竖直长方形后,还能摆放水平的长方形。要满足条件,必须连续的空余部分是偶数。
long long f[N][M]; //f[i][j] 在第i行,放竖直长方形时的状态的集合。
int main() {
while (cin >> n >> m, n || m) {
memset(f, 0, sizeof f);
for (int i = 0; i < 1 << m; ++i) {
st[i] = true;
int cnt = 0;
for (int j = 0; j < m; ++j) { //从左列边界开始
if (i >> j & 1) { //摆放了竖直长方形,则判断之前连续的空余部分是否是偶数
if (cnt & 1) st[i] = false; //不是偶数,则说明,该状态无效
cnt = 0; //连续空余部分的数量清0
} else cnt++; //当没有摆放竖直长方形时,空余部分累加
}
if (cnt & 1) st[i] = false; //到右列边界时还需要判断一下
}
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]) //上一行放的竖直长方形与当前要放的竖直长方形不重叠。以及摆放完后当前行连续空余的部分有效。
f[i][j] += f[i - 1][k];
}
}
}
cout << f[n][0] << endl; //最后输出在第n行不摆放竖直小方块时的方案数。
}
return 0;
}

浙公网安备 33010602011771号