网上有一些巧妙的做法,两三个推理式就能解决,但总感觉看不懂、不放心。还是枚举推理清楚、放心。
不过数字大了枚举就不方便了(比如说大于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])。所以需要减掉两个。