题面
题目描述
有一个W行H列的广场,需要用1*2小砖铺盖,小砖之间互相不能重叠,问有多少种不同的铺法?
输入格式
只有一行2个整数,分别为W和H,(1<=W,H<=11)
输出格式
只有1个整数,为所有的铺法数。
样例数据
input
2 4
output
5
题解
经典状态压缩例题。h位二进制数代表某一行的状态:1代表是竖砖的上半部分,0代表其他情况。
所以状态
f[i][j]
f
[
i
]
[
j
]
代表第i行的情况为j的时候,前i行的方案数
观察题目,我们发现只有i-1行能够对i行进行转移。
设j为第i行要转移的状态,k是第i-1行的状态。
f[i-1][k]能转移到f[i][j]的条件是:
首先k和l都合法。
其次,k&l==0
最后,k|l也要是合法的状态。否则在竖块之间插入不了横块。
所以我们先预处理出所有可能的状态放进集合S。
可能的状态满足:两个相邻的1之间的0的个数是偶数。
那么状态转移方程:
f[i][j]=∑f[i−1][k] in_s[j|k]
f
[
i
]
[
j
]
=
∑
f
[
i
−
1
]
[
k
]
i
n
_
s
[
j
|
k
]
j|k==0
code
#include<bits/stdc++.h>
using namespace std;
inline long long read(){
long long num=0;
char c=' ';
bool flag=true;
for(;c>'9'||c<'0';c=getchar())
if(c=='-')
flag=false;
for(;c>='0'&&c<='9';num=num*10+c-48,c=getchar());
return flag ? num : -num;
}
const int maxn=13;
long long n,m,f[maxn][1<<maxn];
bool in_s[1<<maxn];
void yuchuli(){
for(int i=0;i<1<<m;i++){
bool incorrect=0,count=0;
for(int j=0;j<m;j++)
if(i>>j&1)incorrect|=count,count=0;
else count^=1;
in_s[i]=count|incorrect ? 0 : 1;
}
}
void dp(){
f[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<1<<m;j++){
f[i][j]=0;
for(int k=0;k<1<<m;k++){
if((j&k)==0&&in_s[j|k])
f[i][j]+=f[i-1][k];
}
}
}
}
int main(){
n=read();m=read();
yuchuli();
dp();
printf("%lld\n",f[n][0]);
return 0;
}