【问题标题】:Understanding dynamic programming approach to solving a problem了解解决问题的动态规划方法
【发布时间】:2021-05-09 14:51:56
【问题描述】:

我正在浏览 Project Euler 编码存档并遇到问题 115,内容如下:

“注意:这是第 114 题的更难版本。

长度为 n 个单位的行具有最小长度的红色块 放置在其上的 m 个单位,使得任何两个红色块(它们是 允许是不同的长度)由至少一个黑色分隔 正方形。

让填充计数函数 F(m, n) 表示路数 可以填满一行。

例如,F(3, 29) = 673135 和 F(3, 30) = 1089155。

即对于m = 3,可以看出n = 30是最小值 填充计数函数首次超过一百万。

同理,对于m = 10,可以验证F(10, 56) = 880711 和 F(10, 57) = 1148904,所以 n = 57 是 填充计数功能首次超过一百万。

对于 m = 50,找到填充计数对应的 n 的最小值 功能首次突破百万。”

我可以使用蛮力方法(使用三个嵌套的 for 循环和中间的大量 while 循环,跨越大约 50 行代码)来解决这个问题。相比之下,我发现了这段代码,利用动态编程:

m, n = 50, 168
ways = [1]*(m) + [0]*(n-m+1)
for k in range(m, n+1):
   ways[k] = ways[k-1] + sum(ways[:k-m]) + 1

ways[n]

现在这对我来说看起来很优雅!我了解代码的技术部分,但我不明白这段代码如何解决问题。希望在这里得到解释性帮助。

【问题讨论】:

标签: python dynamic-programming


【解决方案1】:

ways[k] 成为长度为k 的行所需的可能性数。对于k = 0k = m - 1,我们不能放置任何红色方块,所以只有一种可能性:什么都不放置。因此,我们用1 初始化ways 的第一个m 值。对于k = m 起,我们可以用第 k 个单元做三件事。首先,我们可以将其设置为黑色。这样做的方式总数与分配给k - 1 的方式的数量相同,因为除了为k - 1 所做的选择之外,我们不会对布局做出任何选择。我们可以做的第二件事是为整个k 长度分配一个巨大的红色块。确实有一种方法可以做到这一点。第三种选择是分配一个不占用整行的红色块。假设在这个新块开始之前的黑色方块(必须总是有一个,因为我们已经讨论了块跨越整个区域的情况)具有索引i。我们知道ii + m < k 为界,因为块的长度必须至少为m,所以减去m 我们得到i < k - m。所以对于这第三种情况,我们要考虑每个有效的i(从i = 0 到但不包括i = k - m),并将所有可能的方式加起来,我们可以在i + 1 开始一个红色块,这由sum(ways[:k-m]) 计算得出。将每个案例相加对应于实施的重复:ways[k] = ways[k-1] + sum(ways[:k-m]) + 1。对于任何n,答案现在位于ways[n]。最后一点,该算法的复杂性可以通过更复杂的数据结构来进一步提高,从而有效地通过更新来回答前缀和查询。

【讨论】:

    猜你喜欢
    • 2021-02-25
    • 1970-01-01
    • 2020-10-24
    • 1970-01-01
    • 2016-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多