//n&1表示如果n是 奇数,则为1;如果是偶数,则为0
//若n=0,则为0(false);若n=1,则为1(true)
//核心:填完横着的,若合法,竖着的就只有一种填法
#include<cstring>
#include<iostream>
using namespace std;
const int N=12,M=1<<N;
int n,m;
long long f[N][M];//f[i][j]表示填满前i列,且第i-1列的状态是j的方法数(i从下标0开始)
//(二进制的j中第k位若是1,表示第i-1列k行的位置上有前一列伸出来的一格,为0则反之)
bool st[M];//表示每种状态是否合法
int main(){
while(scanf("%d%d",&n,&m)!=EOF&&n!=0){//读入
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;//空格个数为奇数
cnt=0;
}
else cnt++;//空格数+1
if(cnt&1) st[i]=false;//判断最后两个伸出来的空格间的空格个数
}
memset(f,0,sizeof(f));
f[0][0]=1;
//dp过程
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)==0&&st[j|k]){
//合法条件
//1.当前这一列和前一列的状态不重合
//2.空格为偶数
f[i][j]+=f[i-1][k];
}
cout<<f[m][0]<<endl;//结果
//填满前m列,且第m列没有一个从前列伸出来(状态的二进制表示为000000)的方法数
}
}
状压dp(蒙德里安的梦想)
最新推荐文章于 2024-07-11 22:03:57 发布