蒙德里安的梦想

发布时间 2023-04-03 22:49:29作者: bothgone

蒙德里安的梦想

题目描述

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

例如当 $ N=2,M=4 $ 时,共有 \(5\) 种方案。当 \(N=2,M=3\) 时,共有 \(3\) 种方案。

如下图所示:

2411_1.jpg

输入格式

输入包含多组测试用例。

每组测试用例占一行,包含两个整数 \(N\)\(M\)

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

输出格式

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

数据范围

$ 1 \le N,M \le 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


算法

(状压DP) \(O(n^2)\)

f[i][j]:第i列摆放横块的状态是j,其中j的定义是:如果当前行有一个横块,则当前二进制状态为1,否则是0

0列的状态j为:1010;第1列的状态j为:0101
每次状态转移是从前一个的状态转移到当前状态的一个合法途径。
合法途径是指:

  • i - 1列与i列没有重叠区域<=>i列的状态j“与”i - 1列的状态k0<=> j & k = 0 ;
  • i列能插入竖块<=>i列不存在连续奇数个0

时间复杂度

dont know

空间复杂度

dont know

C++ 代码

#include<bits/stdc++.h>
using namespace std;
const int N = 12, M = 1 << N;
bool st[M];
long long f[N][M];
int n, m;


void cntstate(int n, int m)
{
    memset(st, false, sizeof st);
    for(int i = 0; i < 1 << n; i ++)
    {
        int cnt = 0;
        st[i] = true;
        for(int j = 0; j < n; j ++)
        {
            if(i >> j & 1)
            {
                if(cnt & 1)
                {
                    st[i] = false;
                    break;
                }
            }
            else cnt ++;
        }
        if(cnt & 1) st[i] = false;
    }
}



int main()
{
    while(cin >> n >> m, n || m)
    {
        cntstate(n, m);
    
        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(int k = 0; k < 1 << n; k ++)
                    if((k & j) == 0 && st[k | j])
                        f[i][j] += f[i - 1][k];


        cout << f[m][0] << endl;
    }
    return 0;
}