找到具有最多 1 的 nXn 子矩阵
Posted
技术标签:
【中文标题】找到具有最多 1 的 nXn 子矩阵【英文标题】:Find the nXn submatrix with the highest number of 1's 【发布时间】:2020-06-30 08:43:13 【问题描述】:我试图解决这个问题但没有成功:
给定 3n X 3n 布尔矩阵,在 O(n^2) 中。
我有一个想法,但它是 O(n^3)。
想法:
计算矩阵中从 (0,0) 开始的 1 并向右移动并仅检查应添加的新列与应删除的列。羽绒也是同样的想法。 每个子矩阵计算是 O(n^2) 并且传递所有矩阵是 O(n) 所以太多了。
我看不到如何通过 3n X 3n 矩阵(即 O(n^2))以及计算 O(1) 中 1 的数量。
有什么想法吗?
为 MBo 编辑:
这是原始矩阵:The martix 在运行两个 for 循环后,CS 看起来像:CS martix
所以如果你想计算从 (0,0) 到 (1,1) 的矩阵,总和将是
sum = CS[1,1] + CS[0,0] - CS[1,0] - CS[0,1] which is 2 + 1 - 2 - 1 = 0
但实际结果应该是 2。
【问题讨论】:
你需要一个Summed-area table 【参考方案1】:首先,正如您所指出的,有 O(n^2) 个大小为 n x n 的子矩阵,实际上是 (2n+1)^2 个子矩阵,因此您需要能够更新 O 中 1 的数量(1),为了得到一个O(n^2)的算法。
为了在沿 3n x 3n 矩阵移动时更新 1 的数量,您必须进行一些预处理,如果没有,显然需要 O(n^3)。这个想法是在每个单元格中存储其右侧和下方的 1 的数量。也就是说,假设您的初始矩阵是 M,那么您将需要两个额外的矩阵:
R[i,j] = M[i,j] + R[i,j+1] (计算其右边 1 的个数)
B[i,j] = M[i,j] + B[i+1,j] (计算下面 1 的个数)
然后,您可以在 O(1) 中计算列或行的任何段的 1 的数量。例如,列的前三分之一中的 1 的数量为 R[0,0] - R[n,0]。这允许您更新 O(1) 中 1 的数量。
例如,假设按照您的建议,您开始计算 (0,0) 矩阵中 1 的数量。假设您将 1 的个数存储在矩阵 Count 中,Count[0,0] 是 (0,0) 矩阵的个数。
那么,
Count[0][1] = Count[0][0] - (R[0][0] - R[n][0]) + (R[0][n] - R[n][n])
也就是说,我们减去第一列前三分之一的 1 的个数,再加上第 n 列(从 0 开始)的前三分之一的 1 的个数。
同样,
Count[1][0] = Count[0][0] - (B[0][0] - B[0][n]) + (B[n][0] - B[n][n])
也就是说,我们减去第一行前三分之一的 1 的个数,再加上第 n 行(从 0 开始)的前三分之一的 1 的个数。
而且,一般来说,
Count[i][j] = Count[i][j-1] - (R[i][j-1] - R[i+n][j-1]) + (R[i][j-1+n] - R[i+n][j-1+n])
或
Count[i][j] = Count[i-1][j] - (B[i-1][j] - B[i-1][j+n]) + (B[i-1+n][j] - B[i-1+n][j+n])
因此,您可以通过沿初始矩阵移动来更新 O(1) 中 1 的数量。这意味着您将能够在 O(n^2) 时间内计算每个子矩阵中 1 的数量。答案将是最大值。
希望对您有所帮助!
为了初始化R矩阵,我们首先赋值最后一列的值,也就是矩阵最后一列的值,然后我们应用递归(R[i,j] = M[i ,j] + R[i,j+1]) 从右到左。
for (i = 0...3n-1)
R[i][3n-1] = M[i][3n-1]
for (j = 3n-2...0) R[i][j] = M[i][j] + R[i][j+1]
与 B 相同,但使用行而不是列,并且从下到上:
for (j = 0...3n-1)
B[3n-1][j] = M[3n-1][j]
for (i = 3n-2...0) B[i][j] = M[i][j] + B[i+1][j]
【讨论】:
您好,非常感谢您的详细解答。您的回答中有一些我不明白的地方,M 是原始矩阵,但是 R 和 B 的初始状态是什么? R[0,0]的值是多少? R[i,j] 表示从位置 j 开始的第 i 行的 1 的个数。我们如何初始化 R?对于所有 i,R[i,3n-1] = M[i, 3n-1]。然后 R[i,j] = M[i,j] + R[i,j+1]。你需要一个双循环:对于每一行,从最后一列开始到第一列,如果你在最后一列,答案是 M[i,j] 否则是 M[i,j] + R [i,j+1]。我编辑我的答案,以便将其包含在代码格式中。 现在我明白了。再次感谢您的解释!【参考方案2】:计算矩阵的累积和
Copy the first row of source matrix A into matrix CS
for every row except for the first one:
for every column:
CS[r][c] = CS[r-1][c] + A[r][c]
for every row:
for every column except for the first one:
CS[r][c] += CS[r][c-1]
现在您可以在 y,x 处找到右下角的任何子矩阵的总和 (省略索引
Sum = CS[y][x] + CS[y - n][x - n] - CS[y-n][x] - CS[y][x-n]
供参考:OpenCV 中的integral image
import random, pprint
a = [[random.randint(0,1) for _ in range(9)] for _ in range(9)]
pprint.PrettyPrinter(indent = 2).pprint(a)
cs = [[0]*10 for _ in range(10)]
for i in range(9):
cs[0][i] = a[0][i]
for r in range(1, 9):
for c in range(9):
cs[r][c] = cs[r-1][c] + a[r][c]
for r in range(9):
for c in range(1, 9):
cs[r][c] += cs[r][c-1]
pprint.PrettyPrinter(indent = 2).pprint(cs)
maxs = -1
for r in range(2, 9):
for c in range(2, 9):
s = cs[r][c] + cs[r - 3][c - 3] - cs[r -3][c] - cs[r][c-3]
if s > maxs:
maxs = s
best = (r, c)
print(maxs, best)
[ [1, 1, 0, 0, 0, 1, 1, 0, 0],
[1, 1, 0, 1, 0, 0, 0, 1, 0],
[0, 1, 0, 1, 0, 1, 0, 1, 0],
[0, 0, 1, 0, 1, 1, 0, 0, 1],
[0, 0, 0, 1, 1, 0, 0, 1, 1],
[0, 1, 1, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 0, 0, 1, 1, 0, 1],
[1, 1, 0, 1, 0, 1, 0, 0, 0],
[1, 0, 0, 1, 0, 0, 1, 1, 1]]
[ [1, 2, 2, 2, 2, 3, 4, 4, 4, 0],
[2, 4, 4, 5, 5, 6, 7, 8, 8, 0],
[2, 5, 5, 7, 7, 9, 10, 12, 12, 0],
[2, 5, 6, 8, 9, 12, 13, 15, 16, 0],
[2, 5, 6, 9, 11, 14, 15, 18, 20, 0],
[2, 6, 8, 11, 13, 16, 17, 20, 23, 0],
[3, 7, 10, 13, 15, 19, 21, 24, 28, 0],
[4, 9, 12, 16, 18, 23, 25, 28, 32, 0],
[5, 10, 13, 18, 20, 25, 28, 32, 37, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
6 (4, 5)
【讨论】:
我觉得你的解决方案不对,我试过了,sum的值不对。请再次查看我的问题(我将对其进行编辑并在您的解决方案中添加一个示例)并告诉我我错过了什么?谢谢 我可能会在索引中犯错,但方法有效 其实我认为指数是正确的,因为它是有道理的,但也许总和计算的方法应该不同? 我更正了索引。在 Python 中,我只添加了零列和行,并且在索引 =-1 时使用它们。找到more canonic code - sumQuery 适合任何语言 现在我明白了..再次感谢!!以上是关于找到具有最多 1 的 nXn 子矩阵的主要内容,如果未能解决你的问题,请参考以下文章
NumPy 2d 数组的切片,或者如何从 nxn 数组 (n>m) 中提取 mxm 子矩阵?