Initialize:
max_so_far = 0
max_ending_here = 0
Loop for each element of the array
(a) max_ending_here = max_ending_here + a[i]
(b) if(max_ending_here < 0)
max_ending_here = 0
(c) if(max_so_far < max_ending_here)
max_so_far = max_ending_here
return max_so_far
任何人都可以帮我理解上述算法的最佳子结构和重叠问题(DP的面包和黄油)吗?
答案 0 :(得分:13)
根据重叠子问题的this定义,Kadane算法(f[i] = max(f[i - 1] + a[i], a[i])
)的递归表达式不会显示此属性。每个子问题只能在一个简单的递归实现中计算一次。
然而,根据其定义here,它确实展示了最佳子结构:我们使用解决方案来解决较小的子问题,以便找到我们给定问题的解决方案(f[i]
使用f[i - 1]
)。
考虑动态编程定义here:
在数学,计算机科学和经济学中,动态规划是一种通过将复杂问题分解为更简单的子问题来解决复杂问题的方法。它适用于表现出重叠子问题1和最佳子结构(如下所述)的特性的问题。如果适用,该方法所花费的时间远远少于不利用子问题重叠的天真方法(如深度优先搜索)。
动态编程背后的想法非常简单。一般来说,为了解决给定的问题,我们需要解决问题的不同部分(子问题),然后结合子问题的解决方案来达到整体解决方案。通常在使用更天真的方法时,会产生并解决许多子问题。动态编程方法只寻求解决每个子问题一次,从而减少计算次数
这为解释Kadane的算法是否可以被视为DP算法留下了空间:它确实通过将其分解为更容易的子问题来解决问题,但其核心递归方法不会产生重叠的子问题, DP是有效处理的 - 所以这将使它超出DP的专长。
另一方面,你可以说基本的递归方法没有必要导致重叠的子问题,但这会使任何递归算法成为DP算法,这会让DP在我看来范围太大。我不知道文献中的任何内容肯定会解决这个问题,所以我不会记下一个学生,也不会以书签或文章的方式对它们进行标记。
所以我会说它不是DP算法,只是一个贪婪和/或递归的算法,具体取决于实现。出于上面列出的原因,我会从算法的角度将其标记为贪婪,但客观上我会认为其他解释同样有效。
答案 1 :(得分:1)
请注意,我的解释来自this answer。它演示了如何将Kadane的算法视为具有重叠子问题的DP算法。
想象一下,我们有一个数组a
,我们想从中获得最大的子数组。为了确定以索引i
结尾的max子数组,以下递归关系成立:
max_subarray_to(i) = max(max_subarray_to(i - 1) + a[i], a[i])
为了获得a
的最大子数组,我们需要为max_subarray_to()
中的每个索引i
计算a
,然后从中获取max()
:
max_subarray = max( for i=1 to n max_subarray_to(i) )
现在,假设我们有一个数组[10, -12, 11, 9]
,我们要从中获取最大的子数组。这将是运行Kadane算法所需的工作:
result = max(max_subarray_to(0), max_subarray_to(1), max_subarray_to(2), max_subarray_to(3))
max_subarray_to(0) = 10 # base case
max_subarray_to(1) = max(max_subarray_to(0) + (-12), -12)
max_subarray_to(2) = max(max_subarray_to(1) + 11, 11)
max_subarray_to(3) = max(max_subarray_to(2) + 9, 49)
如您所见,除最后一个索引max_subarray_to()
外,每个i
的{{1}}都会被评估两次,因此表明Kadane的算法确实存在重叠的子问题
Kadane的算法通常使用自下而上的DP方法来实现,以利用重叠的子问题,并且每个子问题仅计算一次,因此将其变为O(n)。