给定一个二维矩阵,计算其子矩形范围内元素的总和,该子矩阵的左上角为 (row1, col1) ,右下角为 (row2, col2)。
示例:
给定 matrix = [
[3, 0, 1, 4, 2],
[5, 6, 3, 2, 1],
[1, 2, 0, 1, 5],
[4, 1, 0, 1, 7],
[1, 0, 3, 0, 5]
]
sumRegion(2, 1, 4, 3) -> 8
sumRegion(1, 1, 2, 2) -> 11
sumRegion(1, 2, 2, 4) -> 12
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/range-sum-query-2d-immutable
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
方法一:前缀和
如果只要求一次当然可以直接采用暴力法,直接计算其和即可,但是对于同一个矩阵多次调用复杂度就会显得很高,对于一维数组,如果我们每次想获得指定起始和结束下标的子序列之和,可以计算
s
u
m
[
i
]
sum[i]
sum[i],那么子序列
[
j
,
i
]
[j, i]
[j,i]的和可以用
s
u
m
[
i
]
−
s
u
m
[
j
−
1
]
sum[i]-sum[j-1]
sum[i]−sum[j−1]表示,那么对于二维的情况,我们是不是也可以采用类似的方式?答案是可以!
首先我们需要计算矩阵的和,我们定义个一个
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示在横纵坐标分别都不大于i和j的区域的元素和如图,点D即为点[i,j]。
通过观察我们不难发现(自己画画图就能理解):
d
p
[
i
]
[
j
]
=
d
p
[
i
]
[
j
−
1
]
+
d
p
[
i
−
1
]
[
j
]
−
d
p
[
i
−
1
]
[
j
−
1
]
+
m
a
t
r
i
x
[
i
−
1
]
[
j
−
1
]
dp[i][j] = dp[i][j-1] + dp[i-1][j] - dp[i-1][j-1] + matrix[i-1][j-1]
dp[i][j]=dp[i][j−1]+dp[i−1][j]−dp[i−1][j−1]+matrix[i−1][j−1]
目前我们得到了这个关于和的矩阵,接下来我们需要找到通过这个矩阵获得任意指定左上角和右下角坐标的矩形区域的和的方法。如图所示,如果想求蓝色区域的和,用dp[4][3],去掉红色区域的和再去掉黄色区域的和那么dp[1][0],也就是蓝色圈出的5的左上区域被减了两次,需要加上,就可以得到蓝色区域的和了。
s
u
m
=
d
p
[
r
o
w
2
]
[
c
o
l
2
]
−
d
p
[
r
o
w
2
]
[
c
o
l
1
−
1
]
−
d
p
[
r
o
w
1
−
1
]
[
c
o
l
2
]
+
d
p
[
r
o
w
1
]
[
c
o
l
1
−
1
]
sum = dp[row2][col2]-dp[row2][col1-1]-dp[row1-1][col2]+dp[row1][col1-1]
sum=dp[row2][col2]−dp[row2][col1−1]−dp[row1−1][col2]+dp[row1][col1−1]
由于在代码中下标需要大于等于0,所以在求dp的时候统一将坐标都加了一。
class NumMatrix {
public:
NumMatrix(vector<vector<int>>& matrix) {
rows = matrix.size();
if(rows==0) return;
cols = matrix[0].size();
dp = vector<vector<int>> (rows+1, vector<int>(cols+1));
for(int i=0; i<rows; ++i)
{
for(int j=0; j<cols; ++j)
{
dp[i+1][j+1] = dp[i][j+1] + dp[i+1][j] - dp[i][j] + matrix[i][j];
}
}
}
int sumRegion(int row1, int col1, int row2, int col2) {
return dp[row2+1][col2+1] - dp[row1][col2+1] - dp[row2+1][col1] + dp[row1][col1];
}
private:
int rows, cols;
vector<vector<int>> dp;
};