最大子数组和之二维数组

基本问题

输入:二维数组num[][],假设二维数组为m行n列

输出:二维数组中最大子数组的和

解法与思路:

1、O(m3n3)算法

即直接对所有子数组进行遍历,并记录最大和。遍历方法:穷举所有子数组的左上角坐标(i,j)和右下角坐标(x,y),然后计算该子数组的和,并保留较大解。由于穷举的子数组个数为O(m2n2),计算子数组和的复杂度为O(mn),所以算法整体复杂度为O(m3n3)

具体代码如下所示:

 1 def maxsum_m3n3(m, n):
 2     maxsofar = 0
 3     for i in range(m):
 4         for j in range(n):
 5             for x in range(i, m):
 6                 for y in range(j, n):
 7                     sumnow = 0
 8                     for a in range(i, x+1):
 9                         for b in range(j, y+1):
10                             sumnow += num[a][b]
11                     if sumnow > maxsofar:
12                         maxsofar = sumnow
13     return maxsofar

2、O(m2n2)算法

可以看到,上述算法可分为两部分:穷举子数组和计算子数组的和,本算法考虑对计算子数组的和进行优化,使用数组预处理来使计算子数组和的复杂度降为O(1)。预处理得到数组presum,presum[i][j]表示左上角坐标(1,1)、右下角坐标(i,j)的子数组的和。这样在计算子数组的和时,便可利用presum[][]的存储结果,左上角坐标(i,j)、右下角坐标(x,y)的子数组的和等于presum[x][y] - presum[x][j-1] - presum[i-1][y] + presum[i-1][j-1]。这是算法复杂度降低为穷举子数组的个数,所以复杂度为O(m2n2)

具体代码如下所示(需确保数组下标不为-1):

 1 def maxsum_m2n2(m, n):
 2     maxsofar = 0
 3     
 4     presum = [[0 for col in range(n)] for row in range(m)]
 5     #数组预处理
 6     for i in range(m):
 7         for j in range(n):
 8             if i == 0 and j == 0:
 9                 presum[i][j] = num[i][j]
10             elif i == 0:
11                 presum[i][j] = presum[i][j-1] + num[i][j]
12             elif j == 0:
13                 presum[i][j] = presum[i-1][j] + num[i][j]
14             else:
15                 presum[i][j] = presum[i-1][j] + presum[i][j-1] - presum[i-1][j-1] + num[i][j]
16     
17     #求解
18     for i in range(m):
19         for j in range(n):
20             for x in range(i, m):
21                 for y in range(j, n):
22                     if i == 0 and j == 0:
23                         sumnow = presum[x][y]
24                     elif i == 0:
25                         sumnow = presum[x][y] - presum[x][j-1]
26                     elif j == 0:
27                         sumnow = presum[x][y] - presum[i-1][y]
28                     else:
29                         sumnow = presum[x][y] - presum[x][j-1] - presum[i-1][y] + presum[i-1][j-1]
30                     maxsofar = max(maxsofar, sumnow)
31 
32     return maxsofar

3、O(m2n)算法

考虑如何利用一维数组求最大和的O(n)算法。可以对二维数组的多行进行压缩,变二维为一维,然后再调用一维的O(n)算法。将左上角坐标为(a,1)、右下角坐标为(c,n)的数组压缩为一维数组com[],一维数组长度为n。为了可以快速实现压缩,引入预处理数组presum[][],presum[i][j]等于num[0][j]+num[1][j]+...+num[i][j],即第j列从1行到i行的和,这样com[j]=presum[c][j]-presum[a][j]。穷举所有可以压缩的二维数组的复杂度为O(m2),调用一维算法的复杂度为O(n),所以算法的整体复杂度为O(m2n)

具体代码如下:

 1 def maxsum_m2n(m, n):
 2     maxsofar = 0
 3     
 4     presum = [[0 for col in range(n)] for row in range(m)]
 5     #预处理
 6     for i in range(m):
 7         for j in range(n):
 8             if i == 0:
 9                 presum[i][j] = num[i][j]
10             else:
11                 presum[i][j] = presum[i-1][j] + num[i][j]
12     
13     #求解
14     com = [0 for col in range(n)]
15     for a in range(m):
16         for c in range(a, m):
17             for i in range(n):
18                 if a == 0:
19                     com[i] = presum[c][i]
20                 else:
21                     com[i] = presum[c][i] - presum[a-1][i]
22             #print max_subseq(com)
23             maxsofar = max(maxsofar, max_subseq(com))
24             
25     return maxsofar

 衍生问题

1、假设数组是在水平方向首尾相连的,此时的二维数组如何求解

解法:将数组有m行n列扩展为m行2n列,在穷举左上角坐标(i,j)、右下角坐标(x,y)的子数组时保证列号的差y-j小于n即可

2、假设数组是在垂直方向首尾相连的,此时的二维数组如何求解

解法:将数组有m行n列扩展为2m行n列,在穷举左上角坐标(i,j)、右下角坐标(x,y)的子数组时保证行号的差x-i小于m即可

3、子数组的定义改为连通子数组,即元素相互连通则称为连通子数组,如何求解最大连通子数组

解法:有待继续思考。。。

下面贴几个连通子数组的测试用例(来自http://www.cnblogs.com/xinz/p/3318230.html

原文地址:https://www.cnblogs.com/zghaobac/p/3390777.html