用1 ×2 的瓷砖覆盖8 ×8 的地板,有多少种方式呢?

这个题在POJ上有,地址是:http://acm.pku.edu.cn/JudgeOnline/problem?id=2411
以前做过这个题,是用状态DP来做的。大致说下方法,DP需要用到两维,一维表示到达哪一行,另一维表示这一行的状态。

用二进制来表示
假定方格为N行M列。
表示方法如下:
f[i][j]。 ---- i表示第i行,j表示该行的二进制状态。而f[i][j]则记录由i,j构成的状态有多少种摆法。

我举个例子,假定总共有4行5列。
如下状态: *号表示被铺了方块,0表示尚未被铺方块。
*****
*0**0
00000
00000
可以看到第2行的状态为*0**0,即用二进制来表示就是10110,即 f[2][22] 来表示。(当然,行号也可以从0开始计)

以上是状态表示方法,有了状态表示方法以后就需要得到一个进行状态转移的递推式。
考虑i行j列的那个格子,有3种状态,第一种是由第i-1行j列铺上来,第二种是 在本行内,与相邻的格子铺的瓷砖,第三种该格子留空,以备下一行来使用。
枚举这一行的格子的每一种状态,即可以得到所需的上一行状态。

举例来说,枚举一个如下状态(*表示该格子是已经被填充的,-表示空格子,其余数字均表示2*1的瓷砖摆法):

1***3**
1-22344

可知本行的状态为 1011111,生成该状态所需要的上一行状态为,0111011。因为只有这样才能满足瓷砖1和瓷砖3的嵌入且又不会留下空格子。

因此可以由本状态进行一次累加:用二进制表示第二维 即 a[i][1011111] += a[i-1][0111011]。

那么,最后 a[N][(1<<M)- 1]即为所得。

POJ的这题的代码(几年前写的,风格很烂,见笑了):
C/C++ code
   
   
#include < stdio.h > #include < string .h > long long s[ 14 ][ 14 ][ 5000 ]; int w,h; int list[ 20 ],v[ 20 ]; void GetList( int step){ if (step > w){ int value1 = 0 ; int value2 = 0 ; int i; for (i = 1 ;i <= w;i ++ ){ value1 <<= 1 ; value2 <<= 1 ; switch (list[i]){ case 0 : value1 |= 1 ; break ; case 1 : value1 |= 1 ; value2 |= 1 ; break ; case 2 : value2 |= 1 ; break ; } } if (h > 1 ) s[w][h][value2] += s[w][h - 1 ][value1]; else s[w][h][value2] = 1 ; return ; } if (h > 1 ) list[step] = 2 ,GetList(step + 1 ); if (step < w) list[step] = list[step + 1 ] = 1 ,GetList(step + 2 ); list[step] = 0 ; GetList(step + 1 ); } int main(){ memset(s, 0 , sizeof s); int i,j,k; v[ 0 ] = 1 ; for (i = 1 ;i < 20 ;i ++ ) v[i] = v[i - 1 ] << 1 ; for (w = 1 ;w <= 12 ;w ++ ) for (h = 1 ;h <= 12 ;h ++ ) GetList( 1 ); for (scanf( " %d%d " , & w, & h);w;scanf( " %d%d " , & w, & h)) printf( " %I64d\n " ,s[w][h][v[w] - 1 ]); }


2953784 zyl072 2411 Accepted 7844K 124MS G++ 1222B 2007-11-29 10:22:08  

有点需要说明,由于这题的测试数据很多,因此我不是每次输入后重新计算,而是选择了一次性把所有可能输入的结果全部算完。。然后再输入后就可以直接输出结果。这样做在测试数据很多的情况下可以避免很多重复运算。因此我的DP状态是用三维来表示的,分别是 行、列、当前行状态。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值