在一次采访中,我被问到是否给了一个n * m矩阵如何计算给定子矩阵中值的总和(由左上角,右下角坐标定义)。
我被告知我可以预处理矩阵。
我被告知矩阵可能是巨大的,因此子矩阵也是如此,因此算法必须高效。我发现了一点,并没有被告知最佳答案。
任何人都有一个好的答案?
答案 0 :(得分:50)
这是Summed Area Tables的用途。 http://en.wikipedia.org/wiki/Summed_area_table
您的“预处理”步骤是构建一个相同大小的新矩阵,其中每个条目是该条目左上角的子矩阵的总和。任何子矩阵和都可以通过查找和混合SAT中的4个条目来计算。
编辑:以下是一个例子。
对于初始矩阵
0 1 4
2 3 2
1 2 7
SAT是
0 1 5
2 6 12
3 9 22
使用S(x,y)= a(x,y)+ S(x-1,y)+ S(x,y-1)-S(x-1,y-1)获得SAT ,
其中S是SAT矩阵,a是初始矩阵。
如果你想要右下2x2子矩阵的总和,答案将是22 + 0 - 3 - 5 = 14.这显然与3 + 2 + 2 + 7相同。无论大小如何对于矩阵,子矩阵的总和可以在4个查找和3个算术运算中找到。构建SAT是O(n),同样每个单元只需要4次查找和3次数学运算。
答案 1 :(得分:5)
您可以通过动态编程来完成。创建大小为n * m的矩阵dp。 并为每个i,j在哪里
1 <= i <= n , 1 <= j <= m
dp[i][j] will be :
dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + values[i][j]
对于每个查询,我们有lx,rx,ly,ry,其中lx和rx是左上角坐标,ly和ry是子矩阵的右下角坐标。
1 ≤ lxi ≤ rx ≤ n, 1 ≤ ly ≤ ry ≤ m
sum = dp[rx][ry] - dp[lx - 1][ry] - dp[rx][ly - 1] + dp[lx-1][ly - 1]
查看图片以了解算法的工作原理。
OD = dp[rx][ry], OB = dp[lx - 1][ry], OC = dp[rx][ly - 1], OA = dp[lx - 1][ly - 1]
答案 2 :(得分:3)
创建一个新矩阵,其中条目(i,j)
是原始矩阵中具有低于或等于i
和j
的元素的总和。然后,要查找子矩阵中元素的总和,您可以使用和矩阵的子矩阵的角来使用恒定数量的基本运算。
特别是找到和矩阵的角top_left,bottom_left,top_right和bottom_right,其中前三个就在子矩阵之外,bottom_right就在里面。然后,你的金额将是
bottom_right + top_left - bottom_left - bottom_right
答案 3 :(得分:1)
以下是使用Summed Area Tables概念的C中的示例实现,如上面的一个答案所述。
同样的Python实现可以在下面的链接中找到 - http://www.ardendertat.com/2011/09/20/programming-interview-questions-2-matrix-region-sum/
#include<stdio.h>
int pre[3][3];
int arr[3][3] = {
{0,1,4},
{2,3,2},
{1,2,7}
};
void preprocess()
{
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
if(i>0 && j>0)
{
pre[i][j] = arr[i][j] + pre[i-1][j] + pre[i][j-1] - pre[i-1][j-1];
}
else if(i>0 && j==0)
{
pre[i][j] = arr[i][j] + pre[i-1][j];
}
else if(j>0 && i==0)
{
pre[i][j] = arr[i][j] + pre[i][j-1];
}
else
{
pre[i][j] = arr[i][j];
}
}
}
}
int subsum(int x1, int y1, int x2, int y2)
{
preprocess();
int ans = pre[x2][y2] - pre[x1-1][y2] - pre[x2][y1-1] + pre[x1-1][y1-1];
return ans;
}
int main()
{
printf("%d\n",subsum(1,1,2,2));
return 0;
}
答案 4 :(得分:0)
这应该有效。你总是必须遍历子矩阵中的每个元素来进行添加,这是最简单的方法。
*请注意,以下代码可能无法编译,但它在伪代码中是正确的
struct Coords{
int x,y;
}
int SumSubMatrix(Coords topleft, Coords bottomright, int** matrix){
int localsum = 0;
for( int i = topleft.x; i <= bottomright.x; i++ ){
for(int j = topleft.y; j <= bottomright.y; j++){
localsum += matrix[i][j];
}
}
return localsum;
}
编辑:另一种预处理方法是从包含行或列总和的原始数据创建另一个矩阵。这是一个例子: 原文:
0 1 4 2 3 2 1 2 7
行矩阵:
0 1 5 2 5 7 1 3 10
列矩阵:
0 1 4 2 4 6 3 6 13
现在,只需获取端点x值并减去起点值,就像这样(基于行):
for( int i = topleft.y; i >= bottomright.y; i++ ){
localsum += matrix2[bottomright.x][i] - matrix2[topleft.x][i];
}
现在,它是O(n)或O(m)