最大化矩阵中“非重叠”数字的总和

时间:2012-04-30 05:05:55

标签: algorithm matrix dynamic-programming mathematical-optimization

只是寻找一些方向,我意识到给出的例子可以使用强力迭代来解决,但我正在寻找一个更优雅(即数学?)的解决方案,它可以解决更大的例子(比如说20x20或30x30)。完全有可能无法做到这一点,而且我在提出一种不依赖蛮力的方法方面收效甚微......

我有一个矩阵(称之为A),它是nxn。我希望从矩阵A中选择一个子集(称之为B)。子集将由n个元素组成,其中从每一行和A的每一列中获取一个且仅一个元素。输出应提供解决方案( B)使得构成B的元素的总和是给定这些约束的最大可能值(例如,在下面的示例中为25)。如果找到B的多个实例(即,给出相同最大总和的不同解),则应选择具有最大最小元素的B的解。

B也可以是nxn的选择矩阵,但只有n个所需元素不为零。

例如: 如果A =

|5 4 3 2 1|
|4 3 2 1 5|
|3 2 1 5 4|
|2 1 5 4 3|
|1 5 4 3 2|

=> B将是

|5 5 5 5 5|

但是,如果A =

|5 4 3|
|4 3 2|
|3 2 1|

B =

|3 3 3|

因为B的最小元素是3,大于

|5 3 1|

|4 4 1|

这两者也总和为9

4 个答案:

答案 0 :(得分:6)

您的问题几乎与Assignment problem相同,例如Hungarian algorithm。在多项式时间内由{{3}}求解。

请注意,赋值问题通常是最小化问题,但是将矩阵乘以-1并添加一些常量应该使该方法适用。此外,对于多个最优解的情况,没有正式的制动条件。但是,该方法可以为您提供具有最佳总和的解决方案。设m是最小加数。通过将所有小于或等于m的条目设置为零来修改矩阵并再次求解。你要么得到一个比上一个更好的总和的解决方案。如果没有,之前的解决方案已经是最优的。

答案 1 :(得分:0)

哎哟。 This algorithm is wrong; there is no proof because it's wrong and therefore it's impossible to prove that it's correct.;)我将它留在这里,因为我太依赖于完全删除它,这很好地证明了为什么你应该正式证明算法,而不是说“这看起来正确!这是不可能的无法工作!“


我暂时没有证据就给出了这个解决方案。我有一个校对草图,但我无法证明这个问题的最佳子结构。总之...

问题

给定一个正方形数组,选择尽可能多的“非重叠”数字,以便最大化所选数字的总和。 “非重叠”表示没有两个数字可以来自同一行或同一列。

算法

  • A成为n by n个数字的正方形数组。
  • Aij表示A行和i列中j的元素。
  • S( i1:i2, j1:j2 )表示A的方形子阵列的非重叠数的最佳和,其中包含行i1i2和列{{1}的交集} j1

然后,非重叠数的最佳和表示为j2,并给出如下:

S( 1:n , 1:n )

也就是说,大小S( 1:n , 1:n ) = max { [ S( 2:n , 2:n ) + A11 ] [ S( 2:n , 1:n-1 ) + A1n ] [ S( 1:n-1 , 2:n ) + An1 ] [ S( 1:n-1 , 1:n-1 ) + Ann ] } (recursively) Note that S( i:i, j:j ) is simply Aij. 的正方形数组的最佳和可以通过分别计算大小为n的四个子阵列中的每一个的最佳和来确定,然后最大化总和子阵列和“遗漏”的元素。

n-1

实施

上面的递归算法提出了递归解决方案:

S for |# # # #|
      |# # # #|
      |# # # #|
      |# # # #|

Is the best of the sums S for:

|#      |      |      #|      |# # #  |       |  # # #|
|  # # #|      |# # #  |      |# # #  |       |  # # #|
|  # # #|      |# # #  |      |# # #  |       |  # # #|
|  # # #|      |# # #  |      |      #|       |#      |

请注意,这会调用def S(A,i1,i2,j1,j2): if (i1 == i2) and (j1==j2): return A[i1][j1] else: return max ( S( A, i1+1, i2, j1+1, j2) + A[i1][j1] ], S( A, i1+1, i2, j1, j2-1) + A[i1][j2] ], S( A, i1, i2-1, j1+1, j2) + A[i2][j1] ], S( A, i1, i2-1, j1, j2-1) + A[i2][j2] ], ) O(4^n)!这比“强力”解决方案的时间S()时间复杂度要好得多,但仍然表现不佳。

这里需要注意的重要一点是许多调用使用相同的参数重复。例如,在求解3 * 3阵列时,每个2 * 2阵列都会被多次求解。

这表明两种可能的加速解决方案:

  • 制作递归函数O(n!)缓存结果,以便每个S()只需S(A,i1,i2,j1,j2)一次。这意味着i1,i2,j1,j2只需要计算S()结果 - 所有其他请求都将从缓存中填充。 (这称为memoising。)
  • 不是从顶部开始,使用较大的O(n^3)数组,而是逐步减少较小的子问题,从底部开始,使用尽可能小的子问题,并将向上构建到{ {1}}案例。这称为动态编程。这也是n*n,但它更快n*n,因为您不必一直点击缓存。

动态编程解决方案有点像:

  • 找到所有1x1子阵列的最佳解决方案。 (普通)。
  • 找到所有2x2子阵列的最佳解决方案。
  • 找到所有3x3子阵列的最佳解决方案。
  • ...
  • 找到所有n-1 * n-1子阵列的最佳解决方案。
  • 找到完整n * n子阵列的最佳解决方案。

关于此解决方案的说明:

  • 还没有证明。我正在努力。
  • 您会注意到上述算法仅为您提供O(n^3),最佳总和。它没有告诉你哪些数字实际构成了这笔钱。您可以添加自己的方法来回溯解决方案的路径。
  • 上述算法不保证像O(n^3)S()这样的关系将被打破,以支持所有个别数字尽可能大(以便{{1} }胜利。)我相信你可以定义2,2来打破关系,支持尽可能多的数字,这将做你想要的,但我无法证明。

一般说明:

Dynamic programming是一种强大的技术,用于为任何具有两个属性的问题设计快速算法:

  1. 最佳子结构:问题可以分解为稍微小一些的部分,每个部分都可以作为解决原始问题的一部分。
  2. 重叠子问题意味着要解决的实际子问题很少,子问题的解决方案会多次重复使用。
  3. 如果问题具有最佳子结构,并且问题分解为稍微较小的问题 - 请将大小1,3的问题分解为大小为2,2的子问题 - 然后问题可以通过动态编程来解决。

    如果您可以将问题拆分为个较小的块 - 请将大小为max()的问题分成两半,每个大小为n - 即除以征服,而不是动态编程。划分和征服解决方案通常非常快 - 例如二进制搜索将在n-1时间内在排序数组中找到一个元素。

答案 2 :(得分:0)

马蒂亚斯表示你应该使用回溯。

  1. 找到合理的解决方案。从每行中选择最大值,看它们是否不重叠。如果没有,则扰乱解决方案的一部分,使结果不重叠。
  2. 定义部分解决方案的适用性。让我们假设您正在迭代地获取每行的值,并且您已经从前k行中选择了值。此解决方案的适用性等于已拾取值的总和+剩余行和未选择列的最大值
  3. 现在递归地开始搜索解决方案。从第一行中选择值,计算它们的适应度并将它们插入优先级队列。删除所有适合度低于当前最佳解决方案的解决方案(在步骤1中初始化)。选择队列头部的解决方案,计算下一级解决方案并将其插回优先级队列。从所有列和行中选择值后,计算总和,如果它高于当前最佳值,则替换它。

答案 3 :(得分:-1)

这与n Queens problem有关,除了您不关心对角线并且您有加权解决方案。作为皇后区的问题,你可以通过(多次)回溯来解决它。

即,一旦找到解决方案,您就会记住它的重量,将该溶液标记为无效,然后重新开始。 (a)权重最高的解决方案获胜。