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