Mondriaan's Dream

Mondriaan's Dream

有一个\(n\times m\)的网格图,用\(1\times 2\)的矩形网格无重叠的摆满的方案数,\(1\leq n,m \leq 11\)

数据范围已经提示可以二进制压缩,考虑到网格图常以行列为阶段,所以自然想到\(dp[i][j]\)表示处理到第i行,第i行的状态为j的方案数。

而对于状态j的解释,最自然的是其中1表示一个竖着的网格,但是注意这样并不能保证竖着的矩形长度正好为2,所以设1为此处为矩形的上半,注意到要保证一行的合法性,我们的保证竖着的矩形之间有偶数个网格,而这个我们可以预处理,设\(a[i]\)表示状态i是否合法,于是我们有

\[dp[i][j]=\sum_{k=0}^{2^m-1}dp[i-1][k](!k\&j\&\&a[k|j]) \]

边界:\(dp[1][i]=1,i=1,2,3,...,2^m-1(a[i])\)

答案:\(\sum_{i=0}^{2^m-1}dp[n][i]\)

参考代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#define il inline
#define ri register
#define ll long long
using namespace std;
ll dp[12][2048];
bool check[2048];
int main(){
    int h,w,li;
    while(scanf("%d%d",&h,&w),h&&w){
        li=(1<<w)-1,memset(dp,0,sizeof(dp));
        memset(check,0,sizeof(check));
        for(ri int i(0),j,k;i<=li;++i){
            for(j=k=0;j<w;++j)
                if(i>>j&1){if(k&1)break;k&=0;}
                else k^=true;
            if(j==w&&!k)check[i]|=true,dp[1][i]=1;
        }
        for(ri int i(2),j,k;i<=h;++i)
            for(j=0;j<=li;++j)
                for(k=0;k<=li;++k)
                    if(!(j&k)&&check[j|k])
                        dp[i][j]+=dp[i-1][k];
        printf("%lld\n",dp[h][0]);
    }
    return 0;
}

posted @ 2019-05-28 21:03  a1b3c7d9  阅读(196)  评论(0编辑  收藏  举报