Algorithm 使用2 x 1和1 x 2多米诺骨牌平铺W x H网格的方法有多种?

Algorithm 使用2 x 1和1 x 2多米诺骨牌平铺W x H网格的方法有多种?,algorithm,language-agnostic,dynamic-programming,combinatorics,Algorithm,Language Agnostic,Dynamic Programming,Combinatorics,编写一个程序,将网格的宽度、W和高度H作为输入,并输出用(2x1)多米诺骨牌平铺W-by-H网格的不同方式的数量 我知道如何用3xn网格解决这个问题,但是为这个问题编写递归公式对我来说太难了 我不知道怎么做 我创建了两个函数F(n)-完全平铺到n的方式和S(n)为3 x n的不完全平铺的方式的数量!但是这里的高度是可变的,我想不出任何东西 限制条件:(0)≤ W+H≤ 22)对于这类问题,有一种非常好的通用方法: 1) 计算前几个值 0 1 0 1 0 1 1 2 3 5 8

编写一个程序,将网格的宽度、W和高度H作为输入,并输出用(2x1)多米诺骨牌平铺W-by-H网格的不同方式的数量

我知道如何用3xn网格解决这个问题,但是为这个问题编写递归公式对我来说太难了

我不知道怎么做

我创建了两个函数F(n)-完全平铺到n的方式和S(n)为3 x n的不完全平铺的方式的数量!但是这里的高度是可变的,我想不出任何东西


限制条件:(0)≤ W+H≤ 22)

对于这类问题,有一种非常好的通用方法:

1) 计算前几个值

0  1  0  1  0  1
1  2  3  5  8 13
0  3  0 11  0 41
1  5 11 36 95 281
0  8  0 95  0 1183
1 13 41 281 1183 6728
2) 在整型序列在线百科全书中搜索这些值。您可以通过输入串联的反对角线来搜索方形数组,也可以对几个大项进行无序搜索。成功:包括涉及余弦的乘积公式

据我所知,没有一种特别好的方法可以直接通过动态规划来实现这一点,比如说,在最小维中的状态数不到指数级。这与一些高等数学有联系。请参阅OEIS条目或中的参考


Kasteleyn的余弦积公式可以用精确的算法实现。事实上,这可以用动态规划来完成

2 cos x = e^ix + e^-ix
那么,Kasteleyn的

(4+2 cos 2pi j/(w+1) + 2 cos 2pi k/(h+1)) 

超过范围1这是发动机罩下的DPish。这个想法是IVlad的:回溯记忆

memo = {}


def count_tilings_recursive(uncovered):
    if len(uncovered) & 1:
        return 0
    if not uncovered:
        return 1
    if uncovered not in memo:
        i, j = min(uncovered)
        memo[uncovered] = count_tilings_recursive(uncovered - {(i, j), (i, j + 1)}) + count_tilings_recursive(uncovered - {(i, j), (i + 1, j)})
    return memo[uncovered]


def count_tilings(m, n):
    return count_tilings_recursive(frozenset((i, j) for i in range(max(m, n)) for j in range(min(m, n))))


print(count_tilings(18, 4))

这里的诀窍是防止州数爆炸。首先,我们总是选择最左边然后是最上面的正方形进行覆盖,因此部分覆盖可以描述为以最左边的最上面顺序连续覆盖的正方形的数量,然后是部分覆盖的#行,总共最多(#行*#列+1)*2^#行状态。其次,我们调整网格的方向,使其列数至少与行数相同,将后一个数字限定为10,以便进行有趣的计算(因为11乘11是微不足道的)。

在代码中编写不同状态之间的关系。高度为
2
的情况相当于斐波那契序列。高度
3
的情况可以用一个包含九个相互递归序列的公式来编写(有点乏味!),或者用一个大小为
9
的向量的线性递归来编写

对于
H>=4
,请看一下这个

R.C.Read,《用多米诺骨牌铺矩形的说明》,斐波那契季刊,18.1(1980),24-27

n*2
网格的情况开始。填充方法的数量是第n个Fibonacci数。为什么?因为有两种方法可以在右端完成网格,要么用一个垂直的多米诺骨牌,要么用两个水平的多米诺骨牌

另一种方法是从一个空网格开始,从左到右填充它。考虑到这一点,我们可以根据其“轮廓”类型(即右侧的边界)对所有可能的不完整网格进行分组

例如,对于
H=2
情况,有两种类型的配置文件,对于
H=3
,有九种类型的配置文件

(这来自本文中的图2)

对于一般情况,当
H>=2
时,我们预先计算了配置文件列表。每个配置文件可以由两位掩码编码。请注意,第一个不应表示完整的列,以便在不产生歧义的情况下表示

为了枚举概要文件类型,同时链接它们,我们尝试将一个domino水平和垂直放置在可用的最左边、最低的单元格中。这会生成另一个配置文件,如果它是新的,我们会将其添加到列表中。这样,每个配置文件最多可以链接到两个配置文件

有关
H
的其他选择,请参见下面的代码。这个过程为我们提供了一个递归“公式”,在填充了
r
多米诺骨牌的不完整网格和填充了
r+1
多米诺骨牌的网格之间。这不是一个真正的公式,而是在
next\u profile
字典中编码的。它相当于本文中的公式
(1.1)
,用于
H=3

从集合导入defaultdict
def消歧(a、b):

如果a==(1带记忆的简单递归,也许一些优化应该适用于
W+H你看过了吗?我看过@LasseV.Karlsen,但双精度对我不好。这是一个在线判断问题,我应该报告精确的积分值!@IVIad我们怎么做?你能提示我吗?我甚至不能考虑状态这完全描述了问题!除了在矩阵上回溯之外,我真的没有更好的主意了。现在我想起来了,维基百科上的三角公式可能也适用于这么小的值。我想先试试。我希望能显示一个循环关系。好吧,我想要的是一个动态规划解决方案或者一个没有精度问题的维度。你能解释一下我们如何通过动态规划来解决它吗?因为多米诺骨牌有两个正方形,我们知道W或H或者两者都是偶数。你总是可以假设你正在处理的维度中有一个是偶数。如果两者都是奇数,那么会有一个正方形未解。这将减少s搜索空间。@rossum如果len(uncovered)&1:return 0(uncovered)
,这就是
的作用。如何定义uncovered区域?我不知道它是什么?@ShubhamSharma一组没有平铺的坐标?
{18, 1, 0, 1, 5, 8, 0, 1, 5, 1, 2, 1, 5, 1, 0, 8, 5, 1, 0, 1}
= 18 + z + z^3 + 5z^4 + 8z^5 + z^7 + 5z^8 + z^9 + 2 z^10 + ...
// part of a C# implementation 

public static BigInteger[] cyclotomic(int n)
{
    BigInteger[] working = nthRoots(n); // x^n - 1

    for (int i = 1; i<n; i++)
        if (n % i == 0)
            working = polynomialExactDivision(working, cyclotomic(i));

    return working;
}
memo = {}


def count_tilings_recursive(uncovered):
    if len(uncovered) & 1:
        return 0
    if not uncovered:
        return 1
    if uncovered not in memo:
        i, j = min(uncovered)
        memo[uncovered] = count_tilings_recursive(uncovered - {(i, j), (i, j + 1)}) + count_tilings_recursive(uncovered - {(i, j), (i + 1, j)})
    return memo[uncovered]


def count_tilings(m, n):
    return count_tilings_recursive(frozenset((i, j) for i in range(max(m, n)) for j in range(min(m, n))))


print(count_tilings(18, 4))