这是与发布的代码一起使用的解释。有两个关键技巧可以有效地完成这项工作:(I)Kadane 算法和(II)使用前缀和。您还需要 (III) 将这些技巧应用于矩阵。
第一部分:Kadane 算法
Kadane 的算法是一种找到具有最大和的连续子序列的方法。让我们从寻找最大连续子序列的蛮力方法开始,然后考虑对其进行优化以获得 Kadane 算法。
假设你有序列:
-1, 2, 3, -2
对于蛮力方法,沿着生成所有可能子序列的序列前进,如下所示。考虑到所有可能性,我们可以在每个步骤中开始、扩展或结束一个列表。
At index 0, we consider appending the -1
-1, 2, 3, -2
^
Possible subsequences:
-1 [sum -1]
At index 1, we consider appending the 2
-1, 2, 3, -2
^
Possible subsequences:
-1 (end) [sum -1]
-1, 2 [sum 1]
2 [sum 2]
At index 2, we consider appending the 3
-1, 2, 3, -2
^
Possible subsequences:
-1, (end) [sum -1]
-1, 2 (end) [sum -1]
2 (end) [sum 2]
-1, 2, 3 [sum 4]
2, 3 [sum 5]
3 [sum 3]
At index 3, we consider appending the -2
-1, 2, 3, -2
^
Possible subsequences:
-1, (end) [sum -1]
-1, 2 (end) [sum 1]
2 (end) [sum 2]
-1, 2 3 (end) [sum 4]
2, 3 (end) [sum 5]
3, (end) [sum 3]
-1, 2, 3, -2 [sum 2]
2, 3, -2 [sum 3]
3, -2 [sum 1]
-2 [sum -2]
对于这种蛮力方法,我们最终选择了总和最好的列表,(2, 3),这就是答案。但是,为了提高效率,请考虑您确实不需要保留每个列表。在没有结束的列表中,你只需要保留最好的,其他的不能做得更好。在已结束的列表中,您可能只需要保留最好的一个,并且前提是它比未结束的列表更好。
因此,您只需一个位置数组和一个总和数组就可以跟踪您需要什么。位置数组定义如下:position[r] = s 跟踪以r 结束并以s 开始的列表。并且,sum[r] 给出了以index r 结尾的子序列的总和。这是 Kadane 算法的优化方法。
再次运行示例以这种方式跟踪我们的进度:
At index 0, we consider appending the -1
-1, 2, 3, -2
^
We start a new subsequence for the first element.
position[0] = 0
sum[0] = -1
At index 1, we consider appending the 2
-1, 2, 3, -2
^
We choose to start a new subsequence because that gives a higher sum than extending.
position[0] = 0 sum[0] = -1
position[1] = 1 sum[1] = 2
At index 2, we consider appending the 3
-1, 2, 3, -2
^
We choose to extend a subsequence because that gives a higher sum than starting a new one.
position[0] = 0 sum[0] = -1
position[1] = 1 sum[1] = 2
position[2] = 1 sum[2] = 5
Again, we choose to extend because that gives a higher sum that starting a new one.
-1, 2, 3, -2
^
position[0] = 0 sum[0] = -1
position[1] = 1 sum[1] = 2
position[2] = 1 sum[2] = 5
positions[3] = 3 sum[3] = 3
同样,最好的和是 5,列表是从索引 1 到索引 2,即 (2, 3)。
第二部分:前缀和
我们希望有一种方法来计算沿行的总和,对于任何起点到任何终点。我想在 O(1) 时间内计算该总和,而不仅仅是相加,这需要 O(m) 时间,其中 m 是总和中的元素数。通过一些预计算,可以实现这一点。这是如何做。假设你有一个矩阵:
a d g
b e h
c f i
你可以预先计算这个矩阵:
a d g
a+b d+e g+h
a+b+c d+e+f g+h+i
完成后,只需减去两个值,您就可以得到从该列中任何起点到终点沿任何列的总和。
第三部分:汇集技巧以找到最大子矩阵
假设您知道最大子矩阵的顶行和底行。你可以这样做:
- 忽略顶行以上的行并忽略底行以下的行
行。
- 剩下的矩阵,考虑使用每列的总和
形成一个序列(有点像代表多行的行)。
(您可以使用前缀快速计算此序列的任何元素
总和方法。)
- 使用 Kadane 的方法找出最佳子序列
顺序。你得到的索引会告诉你左右
最佳子矩阵的位置。
现在,实际计算出顶行和底行怎么样?尝试所有的可能性。尝试将顶部放置在任何可以放置的位置,并将底部放置在任何可以放置的位置,并针对每种可能性运行前面描述的 Kadane-base 程序。当您找到最大值时,您会跟踪顶部和底部位置。
查找行和列需要 O(M^2),其中 M 是行数。查找列需要 O(N) 时间,其中 N 是列数。所以总时间是 O(M^2 * N)。并且,如果 M=N,则所需时间为 O(N^3)。