蒙德里安的梦想
蒙德里安的梦想
如果横的放置确定了,那么纵向的肯定也确定了。所以只需要计算横着的方案数即可。
考虑使用 \(f[i][j]\) 表示当前已经处理到了第 \(i\) 列,状态为 \(j\)(状态表示这一列的每个位置是否被长方形占据了)。然后转移的话可以从上一列的许多状态中转移,但是需要满足两个条件
- 保证当前状态和上一列的状态的交集为空,为什么呢?如果这个状态某个位置有,那就说明这个位置的前一列延伸出一个长方形,同理前一列的前一列也延伸出一个,就重叠了。
- 二者的并集(就是第 \(i-1\) 列所有被占据的格子)中不能有连续奇数个 \(0\),这比较好理解,如果有,那么就不能使用纵向小方格填补空白了。
#include<bits/stdc++.h>
using namespace std;
const int N=12;
int n,m;
long long f[N][1<<N];
bool st[1<<N];
int main(){
while(scanf("%d%d",&n,&m),n|m){
memset(f,0,sizeof f);
for(int i=0;i<1<<n;++i){
st[i]=1;
int cnt=0;
for(int j=0;j<n;++j)
if(i>>j&1){
if(cnt&1)st[i]=0;
cnt=0;
}
else ++cnt;
if(cnt&1)st[i]=0;
}
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(!(j&k)&&st[j|k])f[i][j]+=f[i-1][k];
printf("%lld\n",f[m][0]);
}
}