转送门:poj 2411 Mondriaan’s Dream
题目大意
给出一个n*m的棋盘,及一个小的矩形1*2,问用这个小的矩形将这个大的棋盘覆盖有多少种方法。
解题思路
因为对应于一个方格来讲,有两种状态放或者不放
对应于一个1*2的矩形来说有三种方式:横着放,竖着放,不放
因为列的数量为指数级别的,行为线性的,所以我们选取大的最为行,小的作为列
所有dp[i][j]就表示第i行状态为j的时候方法数
那么很明显dp[i][nowsta] = {dp[i-1][presta]}
对于每种2*1的矩形我们有下面的三种方式
1 横放
如果第i行第d列我们选择横放,那么第i行的第d列及d+1列都是1了,第i-1行第d列及d+1列也都必须为1(保证是满的),及状态转移为:
d=d+2,curstate=curstate<<2|3,prestate=prestate<<2|3.
2竖放
第i行第d列我们选择竖放,那么第i行第d列为1,第i-1行d列必须是0,(因为我们是竖着放的,如果前一行不是空的如何能放下呢),状态转移:
d=d+1,curstate=curstate<<1|1,prestate=prestate<<1.
3不放
第i行第d列不妨,那么第i-1行d列肯定是1,(保证是满的),状态转移:
d=d+1,curstate=curstate<<1,prestate=prestate<<1|1.
这个题目采用记忆化搜索,对已经计算出的状态值方法记录,还有就是初始化的时候将dp[0][2<
AC代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 12;
long long dp[MAXN][1<<MAXN];
int col,row;
void dfs(int r,int c,int preSta,int nowSta)
{
if(c == col)
{
dp[r][nowSta]+=dp[r-1][preSta];
return ;
}
if(c+1<=col)
{
dfs(r,c+1,preSta<<1,nowSta<<1|1);
dfs(r,c+1,preSta<<1|1,nowSta<<1);
}
if(c+2<=col)
dfs(r,c+2,preSta<<2|3,nowSta<<2|3);
}
int main()
{
while(~scanf("%d%d",&col,&row) && row&&col)
{
if((col*row)%2)
{
printf("0\n");
continue;
}
if(col>row) swap(col,row);
memset(dp,0,sizeof dp);
dp[0][(1<<col)-1] = 1;
for(int i=1;i<=row;i++) dfs(i,0,0,0);
printf("%lld\n",dp[row][(1<<col)-1]);
}
return 0;
}