【问题标题】:The maximal sum of a rectangular sub-array一个矩形子阵的最大和
【发布时间】:2012-04-05 02:25:30
【问题描述】:

给定一个实数值数组A[1..n,1..n],我希望找到子数组

B = A[i..j,s..t]

1 <= i <= j <= n,1 <= s <= t <= n

使得B 中的数字总和最大化。是否可以使用动态编程来解决这个问题?我与奥胡斯大学的一位 OR 教授交谈,他不知道该怎么做,并说他很难理解如何才能获得最佳的子结构质量。

但有可能吗?如果是,如何?如果不是,为什么?

我已经知道在O(n^3) 时间运行的算法,通过将其减少到n(n+1)/2 复杂性O(n) 的子问题,但这似乎有点慢。我知道最优算法会在Omega(n) 时间运行,但我希望可以使用动态编程使其在O(n^2) 时间运行。

原始问题总结

我添加此部分是因为我觉得有些人误解了我的问题的重点。原来的问题是:

  1. 是否可以在O(n^2)时间使用动态规划来解决上述问题?如果是,如何?如果没有,为什么不呢?

其他问题:

我在这里添加了一个新问题。稍后可能会添加更多内容:

  1. 为了使用动态规划,我需要使用易于解决的子问题的解决方案(否则这一点没有实际意义)。问题的结构是这样的,如果我们取A[1..n,1..n] 的子数组B = A[1..m,1..m] 其中m < n,那么数组B 的最优解至多与A 一样好,很简单,因为同样的解决方案在A 中是可行的。使用动态规划,因此有理由问:A[1..i,1..i]的最优子数组和A[1..i+1,1..i+1]的最优子数组有什么关系?

【问题讨论】:

  • 噢,多么令人兴奋。 @izomorphius 会在接下来的 24 小时内死去,让我们像可怜的老费马一样提心吊胆几个世纪!我不希望,但我担心他正在诱惑命运。我明天再来看看。
  • @HighPerformanceMark 我当然希望不会在接下来的 24 小时内死去,但如果发生这种情况,我将返回并困扰 SO 网站,直到一个聪明人写下解决方案并让我饱受折磨的灵魂得到永恒的安息。
  • @Undreren:我没有这种感觉,我只是担心izomorphius。
  • @izomorphius : 好的一面是,你在回家的路上过早死亡对我们其他人来说不再那么麻烦了;)

标签: arrays algorithm dynamic-programming


【解决方案1】:

当您可以计算出分数不可能超过当前最佳时,一个可能有用的优化是跳过检查 a、b 对。

例如,这样做的一种方法是:

  1. 对每一行运行 Kadane 算法(n 次重复 O(n) 算法 = O(n^2))并将最大值存储在数组 M 中。
  2. 在 O(n) 时间内计算数组 M 的垂直前缀和
  3. 现在,对于每个 a、b 对,我们可以使用我们的垂直前缀总和来获得可以从该配对获得的总和的上限,如果它低于我们当前的最佳值,则跳过测试。

如果您还对数组 M 运行 Kadane 的算法并首先测试结果 a,b 对,这可能效果最好。

在最好的情况下(例如图像包含黑色背景和内部某处的白色矩形),这将在 O(n^2) 中找到答案,但对于更复杂的输入,它仍然需要 O(n^3) .

警告:在实践中,这个技巧可能只对极少数输入有帮助,代价是减慢大多数输入......

编辑: 一些额外的解释:

对于第 i 行,M[i] 包含可以从 A[i..i,x..y] 形式的任何高度为 1 的矩形中获得的最大值。

我们定义了一个新的数组P[i](在上面的描述中称为垂直前缀和)。

P[0]=0
P[i+1]=M[i]+P[i]

对于给定的行 s 和 t,我们可以通过计算 sum(M[ i] 对于范围内的 i (s,t+1))。这实际上给了我们一个形状的值,比如:

...           Row s
 ....
 .......
   ....       Row t

通过从 s 和 t 之间的每一行中获取最佳高度 1 的矩形来形成。

数组 P[i] 很有用,因为 P[i] = sum(M[j] for j in range(i)),所以我们可以计算 sum(M[i] for i in range(s,t +1)) = P[t+1]-P[s] 在 O(1) 时间内。

【讨论】:

  • 垂直前缀总和是什么意思?
  • P[i] = sum( M[j] for j in range(i) )
  • 我听不懂你在说什么。 i 是什么?
【解决方案2】:

来自The Algorithmist,如果你有一个 n × n 数组,你能做的最好的就是 O(n^3):

  • 首先,计算所有列的垂直前缀总和(O(n^2) 算法)。
  • 其次,假设最大子数组将在 a 行和 b 行之间,包括两端。只有 O(n^2) a, b 对使得 a
  • 因为我们已经有了所有列的垂直前缀总和,所以可以在 O(1) 时间内计算列 c 的 arr[a..b][c] 中的元素总和。这使我们可以将每一列的总和想象为跨所有列的一维数组的单个元素(一维数组,一行 n 列)。
  • 有一种 O(n) 算法可以计算一维数组的最大子数组,称为 Kadane 算法。
  • 在每个 a 和 b 组合中应用 Kadane 算法,总复杂度为 O(n3)。

鉴于您有一个 n × 2 数组,您可以将其降低到 O(n^2)。如上,关键是使用Kadane's algorithm

【讨论】:

  • 事实上有证据证明O(n^3) 是一个严格的上限吗?
  • 另外,这篇文章真的没什么用,因为它只是总结了我已经知道的O(n^3)算法。最后一部分是微不足道的。如果你固定一维,那么使用 Kadane 算法的一维数组的数量是微不足道的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-18
  • 1970-01-01
  • 2012-10-17
  • 1970-01-01
  • 1970-01-01
  • 2011-02-08
相关资源
最近更新 更多