tiling with dominoes,一种啰嗦但清楚的dp推理

网上有一些巧妙的做法,两三个推理式就能解决,但总感觉看不懂、不放心。还是枚举推理清楚、放心。
不过数字大了枚举就不方便了(比如说大于24),而且枚举会出现重复的情况。

由于domino的大小为1x1x2,这决定了dp的关联长度最小可以为1(网上有些做法关联长度为2,当然也是可以的),也就是说各推理式里不会出现n-2或是更早的项,只会出现n和n-1。
我们按从左到右、从上到下的顺序来摆放,以确保我们不会留下空隙。如果一层没有摆满,那么下一个块至少有一部分在这一层(而不能全部位于新的一层),否则就违背了我们约定的顺序。
在考虑推理式时,可以这样想象:等式的左边称为目标态,我们从目标态中敲掉一个domino,然后再马上放回原处。如果这个过程没有违背顺序约定,那么敲掉后的状态可以视为目标态的来源状态。

其实原题并没有限制摆放顺序,不过我觉得按照这个顺序摆放非常合理,也简化了问题。

2维,棋盘宽度为1x4

详细的问题描述和答案:https://www.cnblogs.com/chenximcm/p/6285143.html
枚举的做法就是从这篇文章学的,我把这种方法运用在以下两题。

2维,棋盘大小为1x3

详细的问题描述和答案:https://www.geeksforgeeks.org/tiling-with-dominoes/
上文中附带的答案的推理式就比较少。如果能看懂的话,就不必再看本文这糟糕的枚举推理了。

需要枚举23种情况。

#include <iostream>
using namespace std;

// mat[n][7] = mat[n-1][0] + mat[n][1] + mat[n][4]
// mat[n][6] = mat[n-1][7] + mat[n-1][1]
// mat[n][5] 不存在
// mat[n][4] = mat[n-1][3]
// mat[n][3] = mat[n-1][7] + mat[n-1][4]
// mat[n][2] 不存在
// mat[n][1] = mat[n-1][6]
// mat[n][0] = mat[n-1][7]

// mat[1][0] = 1
// mat[1][1] = 0
// mat[1][3] = 1
// mat[1][4] = 0
// mat[1][6] = 1
// mat[1][7] = 0

int mat[100010][8];

int main() {
    mat[1][0] = 1;
    mat[1][1] = 0;
    mat[1][3] = 1;
    mat[1][4] = 0;
    mat[1][6] = 1;
    mat[1][7] = 0;

    int n=12;

    for(int i=2;i<=n;i++) {
        mat[i][6] = mat[i-1][7] + mat[i-1][1];
        mat[i][4] = mat[i-1][3];
        mat[i][3] = mat[i-1][7] + mat[i-1][4];
        mat[i][1] = mat[i-1][6];
        mat[i][0] = mat[i-1][7];
        mat[i][7] = mat[i-1][0] + mat[i][1] + mat[i][4];
    }
    
    cout<<mat[n][7]<<endl;
}

3维,棋盘大小为2x2

https://www.1point3acres.com/bbs/thread-835608-1-1.html
下面是我的推导式,其中第一条式子的减法是因为重复,重复的情况稍后说明。

// mat[n][15] = mat[n-1][0] + mat[n][12] + mat[n][5] + mat[n][3] + mat[n][10] - 2*mat[n-1][15]
// mat[n][14] = mat[n][4] + mat[n][2]
// mat[n][13] = mat[n][8] + mat[n][1]
// mat[n][12] = mat[n-1][15] + mat[n-1][3]
// mat[n][11] = mat[n][8] + mat[n][1]
// mat[n][10] = mat[n-1][15] + mat[n-1][5]
// mat[n][9] 不存在
// mat[n][8] = mat[n-1][7]
// mat[n][7] = mat[n][4] + mat[n][2]
// mat[n][6] 不存在
// mat[n][5] = mat[n-1][15] + mat[n-1][10]
// mat[n][4] = mat[n-1][11]
// mat[n][3] = mat[n-1][15] + mat[n-1][12]
// mat[n][2] = mat[n-1][13]
// mat[n][1] = mat[n-1][14]
// mat[n][0] = mat[n-1][15]

// mat[1][0] = 1
// mat[1][1] = 0
// mat[1][2] = 0
// mat[1][3] = 1
// mat[1][4] = 0
// mat[1][5] = 1
// mat[1][7] = 0
// mat[1][8] = 0
// mat[1][10] = 1
// mat[1][11] = 0
// mat[1][12] = 1
// mat[1][13] = 0
// mat[1][14] = 0
// mat[1][15] = 2 

n=2时有9种情况,如下图。
在这里插入图片描述
第一种和第七种情况,既可以看成mat[i-1][12]加上一块后得到的,也可以看成是mat[i-1][3]加上一块后得到的。这就导致了重复。12和3有一部分重复(mat[i-1][15]),5和10有一部分重复(mat[i-1][15])。所以需要减掉两个。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值