开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 26 天,点击查看活动详情
蒙德里安的梦想
题目描述
求把 的棋盘分割成若干个 的长方形,有多少种方案。
例如当 时,共有 种方案。当 时,共有 33 种方案。
如下图所示:
输入格式
输入包含多组测试用例。
每组测试用例占一行,包含两个整数 和 。
当输入用例 时,表示输入终止,且该用例无需处理。
输出格式
每个测试用例输出一个结果,每个结果占一行。
数据范围
输入样例:
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
题目分析
今天提前开始一道状态压缩的题目。
首先我们只考虑长方形的横向排列,当横向排列合理化时,竖向排列具有唯一性。
定义 表示前 层已经完全排布,第 列的状态为 的方案数。
定义此处的状态为,第 列从上到下按有无填充为 ,当第 层有填充时意味着第 层也为填充,且属于同一长方形。将第 列的 排序以二进制排列作为状态储存。
对第 层的填充考虑状态的合理性,首先第 层每两个填充及头尾间的空格为偶数,由于第 层为填充则第 层也应填充,则第 层也需考虑其合理性,即第 层的伸出和第 层的伸入(对第 层而言)不能相冲突。
由于复杂度的限制,可以提前预处理每层的合理性。
Accept代码
#include <bits/stdc++.h>
using namespace std;
const int N = 12, M = 1 << N;
int n, m;
long long f[N][M];
bool st[M];
vector<int> sta[M];
int main()
{
while (cin >> n >> m, n)
{
for (int i = 0; i < 1 << n; i ++)
{
bool flg = true;
int cnt = 0;
for (int j = 0; j < n; j ++)
{
if (1 << j & i)
{
if (cnt & 1)
{
flg = false;
break;
}
}
else cnt ++;
}
st[i] = flg && !(cnt & 1);
}
for (int i = 0; i < 1 << n; i ++)
{
sta[i].clear();
for (int j = 0; j < 1 << n; j ++)
{
if (!(i & j) && st[i | j]) sta[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 (auto k : sta[j])
f[i][j] += f[i - 1][k];
cout << f[m][0] << "\n";
}
return 0;
}