只是寻找一些方向,我意识到给出的例子可以使用强力迭代来解决,但我正在寻找一个更优雅(即数学?)的解决方案,它可以解决更大的例子(比如说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
答案 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
的方形子阵列的非重叠数的最佳和,其中包含行i1
到i2
和列{{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
,因为您不必一直点击缓存。动态编程解决方案有点像:
O(n^3)
,最佳总和。它没有告诉你哪些数字实际构成了这笔钱。您可以添加自己的方法来回溯解决方案的路径。O(n^3)
与S()
这样的关系将被打破,以支持所有个别数字尽可能大(以便{{1} }胜利。)我相信你可以定义2,2
来打破关系,支持尽可能多的数字,这将做你想要的,但我无法证明。Dynamic programming是一种强大的技术,用于为任何具有两个属性的问题设计快速算法:
如果问题具有最佳子结构,并且问题分解为稍微较小的问题 - 请将大小1,3
的问题分解为大小为2,2
的子问题 - 然后问题可以通过动态编程来解决。
如果您可以将问题拆分为多个较小的块 - 请将大小为max()
的问题分成两半,每个大小为n
- 即除以征服,而不是动态编程。划分和征服解决方案通常非常快 - 例如二进制搜索将在n-1
时间内在排序数组中找到一个元素。
答案 2 :(得分:0)
马蒂亚斯表示你应该使用回溯。
答案 3 :(得分:-1)
这与n Queens problem有关,除了您不关心对角线并且您有加权解决方案。作为皇后区的问题,你可以通过(多次)回溯来解决它。
即,一旦找到解决方案,您就会记住它的重量,将该溶液标记为无效,然后重新开始。 (a)权重最高的解决方案获胜。