假设您有一个函数quality(x)
,它返回一系列字母x
的质量。给出一个字符串,例如" howareyoutoday,"什么是确定细分的最有效方法"你今天怎么样?#34; (即质量(如何)+质量(是)+质量(你)+质量(今天)是可能的最高质量)?
我在想我们可能会有以下内容:
A[0] = h, A[1] = o, ..., A[n] = y
Q[0] = quality(A[0]), Q[1] = quality(A[0]A[1]), ..., Q[n] = quality(A[0]...A[n])
现在确定分段,我们找到max {Q [0],..,Q [n]},它将返回一些Q [i](第一个空格在此之后)。然后,我们找到max {Q [i + 1],.. Q [n]},它返回另一个Q [i](第二个空格在此之后)等,直到max返回Q [n]。
我有两个问题:这种方法是否使用动态编程?在我看来它确实如此,因为我们用原始问题的子问题构建初始Q.此外,这是一个最佳解决方案吗?根据我的理解,最坏的情况是O(n ^ 2),当max返回Q [0],然后Q [1],然后Q [2]等。
答案 0 :(得分:1)
如果可以使用子问题的缓存结果,则可以使用动态编程。例如,如果质量(A [0] A [1])是质量(A [0])和质量(A [1])的函数(例如质量(A [0] A [1])=质量( A [0])+质量(A [1]))然后你可以通过缓存子问题的结果获益;如果质量(A [0] A [1])与质量(A [0])和质量(A [1])无关,那么你可以通过缓存子问题的结果来获益,你可以& #39; t使用动态编程。
算法的最坏情况是O(n ^ 2)。您可能能够使用潜在的次优O(n)解决方案:测试质量(A [0]),然后质量(A [0] A [1]),然后质量(A [0] A [ 1] A [2]),等等;当当前解决方案的质量低于先前解决方案的质量时停止,因此如果质量(A [0] A [1])>质量(A [0] A [1] A [2])然后在A [0] A [1]分区并开始搜索A [2]处的下一个字。通过增加前瞻,可以以更大的常数因子为代价来提高算法的质量;如果质量(A [0] A [1])> 1,则刚刚描述的算法具有先行(1),而先行(2)算法将在A [0] A [1]处分割。质量(A [0] A [1] A [2])和质量(A [0] A [1])>质量(A [0] A [1] A [2] A [3])。
答案 1 :(得分:1)
将此公式表达为DP问题的一种方法是计算函数f(i):
f(i) = The highest sum of quality scores achievable on the first i characters.
我们注意到最后一段必须在位置i-1处结束,并且它可以在它之前的任何地方开始,直到位置0(假设我们将位置从0开始编号)。让我们称这个段j的起始位置;我们需要尝试j的所有可能值,并找到最好的值。我们应该将最后一段A [j] ... A [i-1]与?通过左边剩下的任何东西的最佳解决方案,这很方便地由f(j)给出。所以我们得到:
f(i) = max { f(j) + quality(A[j]..A[i-1]) } over all 0 <= j <= i.
以f(0)= 0作为边界情况。
f(n + 1)将在二次时间内使用线性存储器要求计算整个字符串的最佳分数。要实际计算单词中断的位置应该看起来得到这个分数,你需要从f(n + 1)向后走,确定哪个j值导致它达到最大值(可能有几个;在这种情况下,任何都可以工作)。继续重复这个,直到你得到j = 0.(或者你可以在计算f()时将这些“最大化”值存储在一个单独的数组中。这个数组通常被称为“pred []”,用于“前任”。)
<强> [编辑] 强>
以上是解决问题的递归。它会正确地解决它,但对于大n来说,非常。所有的DP算法都可以用这种方式说明,但要实际将它从普通的递归转换为DP算法,你需要memoize它,或者弄清楚如何从它做一个自下而上的表填充迭代算法。递归的记忆是微不足道的:你需要做的就是,每当函数计算出i的新值的答案时,存储这个答案(这里是一个大小为n + 1的数组),并在下次使用它时要求f(i)的特定值。自下而上的DP涉及确定可以填充表的顺序,以便满足所有依赖性。这可以(一个常数因素)更快,但有时更难。