以总和最大区间问题为例:
给定一个实数序列,设计一个最有效的算法,找到一个总和最大的区间
def solve(arr):
sum_tmp = 0
sum_max = arr[0]
left = 0
right = 0
left_tmp = 0
for i in range(len(arr)):
sum_tmp += arr[i]
if sum_tmp > sum_max:
sum_max = sum_tmp
right = i
left = left_tmp
if sum_tmp < 0:
left_tmp = i + 1
sum_tmp = 0
return sum_max, arr[left: right]
def solve2(arr):
# if we should find the left and right bound, we can use additional O(n) to store the left bound, and the right bound is the position which is max number in dp
dp = [0 * len(arr)]
dp[0] = arr[0]
for i in range(1, len(arr)):
dp[i] = max(dp[i - 1] + arr[i], arr[i])
return max(dp)
指定区间 [ l , r ] = S [ r ] − S [ l − 1 ] [l, r] = S[r] - S[l - 1] [l,r]=S[r]−S[l−1],其中 S [ i ] S[i] S[i]表示从第一个下表到i的区间和,因此使用一个哈希表存储该值即可,其中的key是区间和,value是下标i,时间复杂度为O(n),空间复杂度为O(n)
思路较简单,这里就不给出代码了。
关键点:求阶二维矩阵的某个矩形区域,可以转化为一维矩阵的最大和区间,具体转化规则如下:
设原矩阵为m行n列,如下:
( a 0 , 0 a 0 , 1 ⋯ a 0 , n a 1 , 0 a 1 , 1 ⋯ a 1 , n ⋮ ⋮ ⋱ ⋮ a m , 0 a m , 1 ⋯ a m , n ) \left( \begin{matrix} a_{0,0} & a_{0,1} & \cdots & a_{0, n} \\ a_{1,0} & a_{1,1} & \cdots & a_{1, n} \\ \vdots & \vdots & \ddots & \vdots \\ a_{m,0} & a_{m,1} & \cdots & a_{m, n} \end{matrix} \right) ⎝ ⎛a0,0a1,0⋮am,0a0,1a1,1⋮am,1⋯⋯⋱⋯a0,na1,n⋮am,n⎠ ⎞
设 S ( i , j ) S(i, j) S(i,j)表示从(0, 0)到(i, j)的子矩阵和,可以得出,求解第i行到第j行的最大矩阵和,等价于求阶一维数组 ( ∑ x = i ′ j a x , 0 , ⋯ , ∑ x = i ′ j a x , n ) , i ′ ∈ [ i , j ] (\sum_{x=i'}^ja_{x, 0}, \cdots, \sum_{x=i'}^ja_{x, n}), i' \in [i, j] (∑x=i′jax,0,⋯,∑x=i′jax,n),i′∈[i,j]的最大区间和,也就是说求解原来的j-i+1行n列的矩阵最大子矩阵和等价于求阶1行n列的数组最大区间和,其中数组的每一项等于原来的矩阵每一列之和,行数可以从1行到j-i+1行
简单理解如下:
S ( i , j ) = ∑ x = 0 i ∑ y = 0 j a x , y S(i, j) = \sum_{x=0}^{i}\sum_{y=0}^{j}a_{x,y} \\ S(i,j)=x=0∑iy=0∑jax,y
上述计算可以先按照列把第0列到第j列按照行相加,得到一行j列的数组,然后按列相加即可得到 S ( i , j ) S(i, j) S(i,j),下式得到的是按列相加得到的一维数组:
( ( a 0 , 0 + a 1 , 0 + ⋯ + a i , 0 ) ( a 0 , 1 + a 1 , 1 + ⋯ + a i , 1 ) ⋯ ( a 0 , j + a 1 , j + ⋯ + a i , j ) ) \left( \begin{matrix} (a_{0, 0} + a_{1, 0} + \cdots + a_{i, 0}) & (a_{0, 1} + a_{1, 1} + \cdots + a_{i, 1}) & \cdots & (a_{0, j} + a_{1, j} + \cdots + a_{i, j}) \end{matrix} \right) ((a0,0+a1,0+⋯+ai,0)(a0,1+a1,1+⋯+ai,1)⋯(a0,j+a1,j+⋯+ai,j))
然后再每个元素相加即得到了 S ( i , j ) S(i, j) S(i,j),因此计算矩阵的最大子矩阵和可以等价于计算多个一维数组的最大区间和。
注:上述一维数组中最外面的括号表示这是一个数组,里面的括号表示这是数组的一个元素
伪代码如下:
def solve(matrix):
max_sum = matrix[0][0]
for i in range(0, len(matrix)):
arr = [0] * len(matrix[0])
for j in range(i, len(matrix[0])):
arr = [arr[k] + matrix[j][k] for k in range(len(arr))]
# find max internal sum in current array
tmp_max_sum, tmp_left, tmp_right = find_max_internal_sum(arr)
if tmp_max_sum > max_sum:
max_sum = tmp_max_sum
c1 = tmp_left
c2 = tmp_right
r1 = i
r2 = j
return [r1, c1, r2, c2]
时间复杂度为 O ( m ∗ n ∗ n ) O(m*n*n) O(m∗n∗n)