6

只是寻找一点方向,我意识到给出的示例可以使用蛮力迭代来解决,但我正在寻找一个更优雅(即数学?)的解决方案,它可能会解决更大的示例(比如 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|

乙 =

|3 3 3|

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

|5 3 1|

或者

|4 4 1|

两者的总和为 9

4

4 回答 4

6

您的问题几乎与分配问题相同,例如可以通过匈牙利算法在多项式时间内解决。

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

于 2012-05-02T08:52:26.567 回答
0

正如马蒂亚斯所说,您应该使用回溯。

  1. 寻找合理的解决方案。从每一行中选择最大值并查看它们是否不重叠。如果不是,则扰动解的一部分,使结果变得不重叠。
  2. 定义部分解决方案的适应度。假设您正在迭代地为每一行获取值,并且您已经从前 k 行中获取了值。该解决方案的适应度等于已选择值的总和 + 剩余行和未选择列的最大值
  3. 现在递归地开始寻找解决方案。从第一行中选择值,计算它们的适应度并将它们插入优先级队列。删除所有适应度低于当前最优解(在步骤 1 中初始化)的解。选择队列头部的解决方案,计算下一级解决方案并将它们插入优先级队列。从所有列和行中选择值后,计算总和,如果高于当前最优值,则替换它。
于 2012-04-30T18:17:43.540 回答
0

哎哟。这个算法是错误的;没有证据,因为它是错误的,因此不可能证明它是正确的。;) 我把它留在这里是因为我太执着于完全删除它,这很好地说明了为什么你应该正式证明算法而不是说“这看起来不错!这不可能失败!”


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

问题

给定一个数字的方阵,选择尽可能多的“非重叠”数字,以使所选数字的总和最大化。“不重叠”意味着没有两个数字可以来自同一行或同一列。

算法

  • A是一个数字的方阵n by n
  • 让表示第 th 行和第 th 列中Aij的元素。Aij
  • 让表示包含行to和列to的交集的S( i1:i2, j1:j2 )方形子数组的非重叠数字的最佳总和。Ai1i2j1j2

然后表示非重叠数的最佳总和,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()!! O(n!)这比“蛮力”解决方案的阶乘时间复杂度要好得多,但性能仍然很差。

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

这为加速提供了两种可能的解决方案:

  • 使递归函数S()缓存结果,以便S(A,i1,i2,j1,j2)每个i1,i2,j1,j2. 这意味着S()只需要计算O(n^3)结果 - 所有其他请求都将从缓存中完成。(这称为记忆。)
  • 与其从大n*n数组的顶部开始,然后逐步处理较小的子问题,不如从可能的最小子问题的底部开始,逐步建立案例n*n。这称为动态规划。这也是O(n^3),但速度要快得多O(n^3),因为您不必一直访问缓存。

动态规划解决方案有点像:

  • 找到所有 1x1 子阵列的最优解。(琐碎的。)
  • 找到所有 2x2 子阵列的最优解。
  • 找到所有 3x3 子阵列的最佳解决方案。
  • ...
  • 找到所有 n-1 * n-1 个子数组的最优解。
  • 找到完整 n*n 子数组的最优解。

此解决方案的注意事项:

  • 还没有证据。我在做这个工作。
  • 你会注意到上面的算法只给你S(),最优的总和。它并没有告诉你哪些数字实际上构成了这个总和。您可以添加自己的方法来追溯解决方案的路径。
  • 上面的算法不能保证像2,2vs.这样的关系1,3会被打破,有利于让所有单独的数字尽可能大(这样才能2,2获胜。)我相信你可以定义max()打破关系以支持最大的数字可能,这会做你想要的,但我无法证明这一点。

一般注意事项:

动态规划是一种强大的技术,可以为任何具有两个属性的问题设计快速算法:

  1. 最优子结构:一个问题可以分解成更小的部分,每个部分都可以作为原始问题解决方案的一部分。
  2. 重叠子问题意味着需要解决的实际子问题很少,并且子问题的解决方案被多次重复使用。

如果问题具有最优子结构,并且问题分解为小的问题(例如,大小问题n分解为大小子问题),n-1则可以通过动态规划来解决问题。

如果您可以将问题分成小的块 - 比如说将大小问题n分成两半,每个大小n/2- 那就是分而治之,而不是动态编程。分而治之的解决方案通常非常快——例如,二分搜索会O(log n)及时在排序数组中找到一个元素。

于 2012-04-30T16:53:14.467 回答
-1

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

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

于 2012-04-30T05:15:47.163 回答