蒙德里安的梦想(26-26)

103 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 26 天,点击查看活动详情

蒙德里安的梦想

题目描述

求把 N×MN×M 的棋盘分割成若干个 1×21×2 的长方形,有多少种方案。

例如当 N=2M=4N=2,M=4 时,共有 55 种方案。当 N=2M=3N=2,M=3 时,共有 33 种方案。

如下图所示:

2411_1.jpg

输入格式

输入包含多组测试用例。

每组测试用例占一行,包含两个整数 NN 和 MM

当输入用例 N=0M=0N=0,M=0 时,表示输入终止,且该用例无需处理。

输出格式

每个测试用例输出一个结果,每个结果占一行。

数据范围

1N,M111≤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

题目分析

今天提前开始一道状态压缩的题目。

首先我们只考虑长方形的横向排列,当横向排列合理化时,竖向排列具有唯一性。

定义 f[i][j]f[i][j] 表示前 i1i-1 层已经完全排布,第 ii 列的状态为 jj 的方案数。

定义此处的状态为,第 ii 列从上到下按有无填充为 0/10/1,当第 ii 层有填充时意味着第 i1i-1 层也为填充,且属于同一长方形。将第 ii 列的 0/10/1 排序以二进制排列作为状态储存。

对第 ii 层的填充考虑状态的合理性,首先第 ii 层每两个填充及头尾间的空格为偶数,由于第 ii 层为填充则第 i1i-1 层也应填充,则第 i1i-1 层也需考虑其合理性,即第 i2i-2 层的伸出和第 ii 层的伸入(对第 i1i-1 层而言)不能相冲突。

由于复杂度的限制,可以提前预处理每层的合理性。

Accept代码 O()O()

#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;
}