动态规划技术应用的子问题的独立性

时间:2012-09-05 15:53:20

标签: algorithm recursion complexity-theory dynamic-programming

通过动态编程技术解决算法的两个标准是

  1. 子问题应该是独立的。
  2. 子问题应重叠。
  3. 我想我理解重叠意味着什么。它基本上意味着子问题具有可能相同的子问题。因此,我们不是一遍又一遍地解决子子问题,而是将其解决一次,将其放在哈希表或数组中,并查看它所需的嵌套时间。但是,第1点即子问题的独立性在这里意味着什么?如果他们有一些共同的子问题,我们如何称他们独立?我的意思是在这个阶段听起来非常违反直觉。

    修改:此crtiteria实际上是given in the famous book: Introduction to Algorithms by CLRS in the Dynamic Programming chapter.

6 个答案:

答案 0 :(得分:1)

请告诉我们您在哪里阅读DP适用于重叠和独立子问题的问题。我不认为这是正确的,因为你给出了同样的直觉原因 - 如果问题重叠,它们就不是独立的。

我经常看到作为Divide-And-Conquer风格算法的标准给出的独立子问题,而我看到重叠的子问题和最优子结构作为动态编程系列的标准给出。 (直观地说,最优子结构意味着更大问题的最佳解决方案由子问题的最佳解决方案组成。经典示例是图形问题中的最短路径:如果您知道从A到B的最短路径经过C,那么你也知道 的一部分从A到B的最短路径经过C恰好是从A到C的最短路径。)

更新:哦,我明白了 - 是的,我猜他们确实提到了独立性。但我并没有像你一样强调这一点。意思是,他们在最佳子结构的更大和更重要的概念的背景下或作为理解的方式提到独立性。

他们的独立意味着,即使两个问题重叠,它们也是“独立的”,因为它们不相互作用 - 一个解决方案并不真正依赖于另一个解决方案。他们实际上使用了我做的相同的例子,最短的路径。最短路径问题的子问题是较小的最短路径问题是独立的:如果从A到B的最短路径经过C,则从A到C的最短路径不使用从C到C的最短路径中的任何边缘。 B.相比之下,最长路径问题不能分享子问题的独立性。

我不认为CLRS提升独立性是错误的,但我认为他们使用的语言有点含糊不清。

答案 1 :(得分:1)

正如CLRS中提供的那样,作者解决了子问题的独立属性和重叠属性之间的区别。他们写道,

“动态编程依赖于独立且重叠的子问题,这似乎有些奇怪。尽管这些要求听起来很矛盾,但它们描述了两个不同的概念,而不是同一轴上的两个点。同一问题的两个子问题是独立的如果两个子问题确实是同一个子问题,而这些子问题是作为不同问题的子问题而出现的,则这两个子问题是重叠的”(CLRS第三版,386)。

答案 2 :(得分:0)

我认为这些标准措辞严厉,因为重叠和独立具有一定的冲突意义。

无论如何能够有效地使用DP方法,你需要

  1. 可以根据更简单的问题递归定义的问题
  2. 部分解决方案的概念,其中剩余部分的解决方案不依赖于你如何达到当前点
  3. 示例:如果要计算从第一行开始在矩阵中移动并且在下一行和相同或相邻列中具有每个步骤时的最大和路径,则可以将其用作“状态”当前的总和,当前行和当前列,因为对于解决方案,用于到达当前位置的路径无关紧要。

    1  4 [3] 2  1  4  9
    2  1 [3] 1  2  3  1
    9 [8] 3  0  1  2  9
    0 [0] 2  4  1  6  3
    1  2 [6] 3  0  4  1
    

    在上面的模式中,此路径的总和为3 + 3 + 8 + 0 + 6。为了最大化总和,您可以观察到从某个点经过的路径的最大值可以获得到达那里的最大值以及从那里到矩阵末端的最大值。因此,解决方案可以在独立的子问题中进行拆分,您可以缓存从矩阵的给定点到结束的最大总和的结果(独立于您如何到达该点)。

    def maxsum(x, y):
        if (x, y) in cache:
            return cache[(x, y)]
    
        if y == height - 1:
            return matrix[y][x]
    
        if x == 0:
            left = -1
        else:
            left = matrix[y][x] + maxsum(x-1, y+1)
    
        center = matrix[y][x] + maxsum(x, y+1)
    
        if x == width-1:
            right = -1
        else:
            right = matrix[y][x] + maxsum(x+1, y+1)
    
        result = cache[(x, y)] = max(left, center, right)
        return result
    

    如果我添加规则,允许不超过三个“9”但是你不能仅使用坐标作为状态,因为跟随子问题(到最后)将受前一个影响(即如何你到达中间位置时已经收集了很多“9”。

    您仍然可以使用动态编程方法,但通过例如将收集的“9”的数字添加到当前状态表示中,可以使用更大的状态空间。

    def maxsum(x, y, number_of_nines):
        if (x, y, number_of_nines) in cache:
            return cache[(x, y, number_of_nines)]
        ...
    

答案 3 :(得分:0)

我的理解是子问题应独立于父更大问题解决。就像回溯中一样,子问题确实取决于您在较大问题中选择的解决方案。

答案 4 :(得分:0)

子问题是独立的。

在分而治之中没有独立性。 例如。在mergesort中。 子问题在划分后合并,这意味着解决方案具有常见的子问题。一切都需要合并,没有一条道路会给出答案。 为了得到最终答案,每个子问题都需要共享子子问题。

  var date_group = [];
  var record_date = [];
    var i;
    for (i = 0; i < data.length; i++) {
      date_group.push(data[i]['date group']);
      record_date.push(data[i]['record date']);
    }

    date_group = _.uniq(date_group);
    record_date = _.uniq(record_date);

  const reportSections = [
    {id: 'date_group1', name: 'record_date1'},
    {id: 'date_group2', name: 'record_date2'},
    {id: 'date_group3', name: 'record_date3'},
    {id: 'date_group4', name: 'record_date4'},
    {id: 'date_group5', name: 'record_date5'},
    {id: 'date_group6', name: 'record_date6'},
  ];

答案 5 :(得分:0)

我不认为子问题应该是依赖的。实际上,如果子问题是独立的,那就太好了,但这不是必须的。

具有依赖子问题的dp问题的一个很好的例子在这里: Paint Houses - Algorithmic problems(油漆屋问题)

在这里,子问题的解决方案取决于前一所房子的颜色。可以通过在dp数组中添加维度并根据前一所房子的颜色构建解决方案来解决这种依赖性。