题意:
将n*m的棋盘分割成若干个1*2的长方形,求方案数。
思路:
考虑按列摆放,某列的各行用0或1表示摆放状态。
若某行是1,表示横放,并且向前一列伸出。
若某行是0,表示竖放,或者由前一列伸出。
状态表示:f[i][j]表示摆放第i列,状态为j时的方案数。
状态转移: ,k是和j状态兼容的状态。
初始状态:f[0][0]=1,第0列不摆放是一种合法的状态
目标状态:f[m][0],第m列没有向下一列伸出的。
预处理:判断合并列的状态i是否合法(合并列就是前一列与当前列按位或)
若合并列的某行是1表示横放,0表示竖放。
若合并列不存在连续的奇数个0,即为合法状态.
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=12;
const int M=1<<N;
bool st[M];
int n,m;
ll f[N][M];
void solve(){
for(int i=0;i<(1<<n);i++){
st[i]=true;
int cnt=0;//记录合并列中连续0的个数;
for(int j=0;j<n;j++){
if((i>>j)&1){
if(cnt%2){
st[i]=false;
break;
}
cnt=0;
}
else
cnt++;
}
if(cnt%2)
st[i]=false;
}
memset(f,0,sizeof(f));
f[0][0]=1;
for(int i=1;i<=m;i++){
for(int j=0;j<(1<<n);j++){//状态:枚举第i列的状态
for(int k=0;k<(1<<n);k++){//状态:枚举第i-1列的状态
//两列状态兼容,不出现重叠的1,不出现连续奇数个0
if((j&k)==0&&st[j|k]){
f[i][j]+=f[i-1][k];
}
}
}
}
printf("%lld\n",f[m][0]);
}
int main(){
while(1){
scanf("%d%d",&n,&m);
if(n==0&&m==0)
break;
solve();
}
}