繁体   English   中英

了解解决问题的动态规划方法

[英]Understanding dynamic programming approach to solving a problem

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

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

一排长度为 n 个单位的行上放置了最小长度为 m 个单位的红色块,这样任何两个红色块(允许长度不同)至少被一个黑色方块隔开。

让填充计数 function, F(m, n) 表示可以填充一行的方式数。

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

也就是说,对于 m = 3,可以看出 n = 30 是填充计数 function 首先超过一百万的最小值。

同理,对于 m = 10,可以验证 F(10, 56) = 880711 和 F(10, 57) = 1148904,因此 n = 57 是填充计数 function 首先超过的最小值一百万。

对于 m = 50,找到填充计数 function 首先超过一百万的 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]

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

ways[k]为长度为k的行所需的可能性数。 对于k = 0k = m - 1 ,我们不能放置任何红色块,所以只有 1 种可能性:什么都不放置。 因此,我们用1初始化前mways的值。 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[:km])计算得出。 将每个案例相加对应于实现的递归: ways[k] = ways[k-1] + sum(ways[:km]) + 1 对于任何n ,答案现在在于ways[n] 最后一点,该算法的复杂性可以通过更复杂的数据结构进一步提高,以有效地通过更新来回答前缀和查询。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM