题目:求把N* M的棋盘分割成若干个1* 2的的长方形,有多少种方案。
例如当N=2,M=4时,共有5种方案。当N=2,M=3时,共有3种方案。
输入格式
输入包含多组测试用例。
每组测试用例占一行,包含两个整数N和M。
当输入用例N=0,M=0时,表示输入终止,且该用例无需处理。
输出格式
每个测试用例输出一个结果,每个结果占一行。
数据范围
1≤N,M≤111≤N,M≤11
输入样例:
1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0
输出样例:
1
0
1
2
3
5
144
51205
题目思路:每行的状态用用十进制等值的二进制表示,1代表当前行有竖着的上部分,0表示其他情况。阶段从第一行开始往下递推,要求下一行的状态与当前按位与全为零(不可能出现全为1的情况,即不可能有相邻两行同列都是一个一个长方形的上一部分),按位或值为零的出现必须是连续的偶数个,每个样例都提前预处理1<<m个数。转移方程dp[i][j]=dp[i-1][k]{pre[j|k]= = 1 && j &k= =0}
边界值dp[0][0]=1,目标dp[n][0](最后一行只能是全部横着放的)
#include<iostream>
#include<cstring>
#include<cstdio>
#define N 12
using namespace std;
long long int n,m,dp[N][1<<N],pre[1<<N];
int main()
{
while(cin>>n>>m&&n)
{
for(int i=0; i<(1<<m); i++)//不能提前一次性处理全部样例,因为状态与m有关。
{
bool cnt=0,tmp=0;
for(int j=0; j<m; j++)
{
if((i>>j)&1) tmp|=cnt,cnt=0;//即使i<(1<<j),仍需计算因为不确定其前几位连续的0是否为偶数
else cnt^=1;
}
pre[i]=tmp|cnt?0:1;
}
memset(dp,0,sizeof(dp));//多组样例、不要忘记初始化
dp[0][0]=1;
for(int i=1; i<=n; i++)
{
for(int j=0; j<(1<<m); j++)
{
for(int k=0; k<(1<<m); k++)
{
if((j&k)==0&&pre[j|k])
{
dp[i][j]+=dp[i-1][k];
}
}
}
}
cout<<dp[n][0]<<endl;
}
}