【问题标题】:Application of dynamic programming动态规划的应用
【发布时间】:2014-08-05 19:02:21
【问题描述】:

我正在为软件开发人员面试做准备,并试图了解如何应用动态编程。我知道一个问题必须满足两个标准——有一个最优的子结构和重叠的子问题。举一个例子,你有一个函数 f(L),它接受一个整数列表 L 并将它映射到另一个整数(例如,f([17, 5, 2, 12] = 35) 你将如何应用动态规划到找到最大值?

例子

L = [17, 5, 2, 12, 9]

你可以有多种f()的组合:

f([17]) + f([5]) + f([2, 12, 19])
f([17]) + f([5, 2]) + f([12, 19])

我通过计算列表中每个元素的 f() 来接近它,例如 f([17])。然后是每两个元素,例如 f([17, 5]),然后是每三个元素,f([5, 2, 12]),等等。然后我将这些值映射到哈希表。接下来,我尝试了 f() 的所有组合以找到最大值。我认为这种方法并不优雅而且有点尴尬。关于如何处理的任何想法?

【问题讨论】:

  • 传递给 f 的值必须在初始列表中连续定位还是允许任意组合?
  • @user2040251 数字必须是连续的。如果没有,则通过 5 个数字的排列更容易解决。
  • @donatello 是的,基本上,如果答案是 f([17]) + f([5, 2, 12]) + f([19]) 你如何使用动态规划来获得这个答案。
  • dp[1] = f([17]),然后 dp[4] 将是 dp[1] + f([5, 2, 12]),最后是 dp[5] = dp[4] + f([19])。当然,也可以计算 dp 的其他值。

标签: algorithm dynamic-programming


【解决方案1】:

进行动态规划的典型方法是创建一个函数,该函数递归地遍历子问题的所有有效排列以给出最终答案,并在进行过程中保存子问题的所有答案(称为memoization),因为这些子答案可能非常可重用。在示例问题的伪代码中:

function F(some_list) {
    ....
}

M = [[]]
L = [17, 5, 2, 12, 9]

function maximize(start, end) {
    // If we have already calculated the highest value
    // for the given slice, why do it again?
    if (M[start][end] != NULL) {
        return M[start][end]
    }

    // The function f applied to the whole slice we are
    // examining is also a viable solution, so we start
    // our hunt there
    max = F(L.slice(start, end))

    // If we are at an end node (that is, we can't
    // divide the problem into smaller pieces) we return
    // and save the value we just calculated
    if (end - start == 1) {
        M[start][end] = max
        return max
    }

    // Let's look at all possible ways we can split the
    // slice we are currently examining
    for (split in range(start + 1, end - 1)) {
        // Let's examine booth parts of the slice and see if
        // the sum of those solutions are better than our
        // previous best solution
        temp_max = maximize(start, split) + maximize(split, end)

        if (temp_max > max) {
            max = temp_max
        }
    }

    // We have examined all possible ways in which we can
    // slice and dice our slice, and found the best
    // solution. Yay! Let's save it for future reference.
    M[start][end] = max
    return max
}

// Examine the whole list
the_great_maximum_value = maximize(0, L.length)

【讨论】:

  • 这看起来像是解决了 f([17, 5]) + f([2, 12, 19]) 之类的情况,但无法检查 f([17] 之类的情况) + f([5, 2]) + f([12]) + f([19])。您将如何检查这种情况?
  • 这也解决了这些场景,当maximize 到达一个结束节点(例如maximize(1, 2) -> f([5]))时,调用函数决定是否包含结束节点会产生更好的值(在这种情况下更高价值)。 maximize 函数总是将问题分成两个新问题,直到到达末端节点,从而检查所有可能的解决方案。
  • 我添加了一些 cmets 可能会让事情变得更清晰。
【解决方案2】:

这是一种可能的方法:

1) 让我们将dp[pos] 定义为通过某种方式(无论如何)拆分列表的第一个pos 元素可以获得的最大值。

2) 基本情况:dp[0] = 0。空前缀就是这种情况。

3)pos > 0:
dp[pos] = max(dp[prev_pos] + f(L[prev_pos + 1], L[prev_pos + 2], ..., L[pos]))
0 <= prev_pos < pos

4)dp[length(L)]的答案。

时间复杂度为O(n^2 * time_to_comptute_f)(因为我们遍历列表中的所有位置,并为每个位置检查O(n)以前的位置,每次都计算f)。

空间复杂度为O(n)

【讨论】:

    猜你喜欢
    • 2012-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-09
    相关资源
    最近更新 更多