棋盘覆盖问题

题目

有一个 N × M ( N ≤ 5 , M ≤ 1000 ) N\times M(N\le5,M\le1000) N×M(N5,M1000)的棋盘,现在有 1 × 2 1\times2 1×2 2 × 1 2\times1 2×1 的小木块无数个,要覆盖整个棋盘,有多少种方式?
答案只需要   m o d   1 0 9 + 7 \bmod 10^9+7 mod109+7 即可。

状压DP简述

状压DP,就是把一个集合中每个元素的状态(一般为 0 / 1 0/1 0/1 )用一个二进制数表示。
对于第 i i i位, 0 0 0代表其中没有 i i i 1 1 1代表其中有 i i i
也可以认为状压DP就是把一个集合压缩为了一个方便操作的整数。

基本集合操作

有两个集合 A , B A,B A,B,其二进制数位 a , b a,b a,b
1.并集 A ⋃ B A\bigcup B AB对应 a a a o r or or b b b
2.交集 A ⋂ B A\bigcap B AB对应 a a a a n d and and b b b
3.补集对应 n o t not not a a a

题目解法

当发现了 N ≤ 5 N\le 5 N5时,就意味着其中一位的状态可以进行压缩。
状态就可以这样设 d p i , S dp_{i,S} dpi,S:前 i i i列全部填满,第 i + 1 i+1 i+1列在之前的填充中填充状态为 S S S的情况数。
这样就利用了 N N N的特性。
对于转移,先枚举 d p i , S dp_{i,S} dpi,S搜索所有状态,把第 i i i列填满,到达状态 d p i + 1 , S ′ dp_{i+1,S'} dpi+1,S
搜索中只要有了空位,就尝试填入 1 × 2 1\times 2 1×2的小木块,
如果有连续两个空位,还能填入 2 × 1 2\times 1 2×1的小木块。

代码

void dfs(int i,int j,int s,int s1,int s2){
    if(j == n){
        if(s1 == (1 << n) - 1)  dp[i + 1][s2] = (1LL * dp[i + 1][s2] + dp[i][s]) % mod;
        return;
    }
    if((s1 & (1 << j)) != 0)    dfs(i,j + 1,s,s1,s2);
    else{
        dfs(i,j + 1,s,s1 + (1 << j),s2 + (1 << j));
        if(j < n - 1 && ((s1 & (1 << (j + 1))) == 0))   dfs(i,j + 2,s,s1 + (1 << j) + (1 << (j + 1)),s2);
    }
}

我的思考

这题是一道难题,需要dp与搜索的完美结合。
我在调试花了许多时间。其中,这题的核心思想是对每一列进行状态压缩、用搜索的方式进行填充和转移。
这里的搜索不仅仅是对下一个状态进行枚举,也是填充这一行的过程。
写搜索时,要明确题目搜索的目的,也可以进行画图思考,这样能帮助我们大大缩短因为不明确思路而造成的大量的调试。
dp的调试重点在明确状态背后的含义(具体的定义),还有一些边界的取值范围。
注意了以上两点,相信能减少调试的次数和时间。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值