Solution:
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, m, q;
cin >> n >> m >> q;
vector<vector<int>> matrix(n + 1, vector<int>(m + 1, 0)); // 整数矩阵
// 输入整数矩阵
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
cin >> matrix[i][j];
}
}
// 计算前缀和
vector<vector<int>> prefixSum(n + 1, vector<int>(m + 1, 0));
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
prefixSum[i][j] = prefixSum[i - 1][j] + prefixSum[i][j - 1] - prefixSum[i - 1][j - 1] + matrix[i][j];
}
}
// 处理询问
while (q--) {
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
int result = prefixSum[x2][y2] - prefixSum[x2][y1 - 1] - prefixSum[x1 - 1][y2] + prefixSum[x1 - 1][y1 - 1];
cout << result << endl;
}
return 0;}
在上一篇的文章中,我们已经介绍了前缀和。
这次的题目就是利用前缀和思想来优化计算子矩阵和的过程。
- 先输入整数矩阵,将其保存在一个二维矩阵
matrix
中。 - 计算
matrix
的前缀和矩阵prefixSum
,其中prefixSum[i][j]
表示矩阵中(1,1)
到(i,j)
所有元素的和。 - 对于每个询问,计算出其子矩阵中所有元素的和。具体做法是,用前缀和矩阵计算出子矩阵的四个角的前缀和,然后利用前缀和的性质计算出子矩阵中所有元素的和。
其中,核心思想:
在计算前缀和矩阵时,可以使用类似于一维前缀和的方式,具体计算公式如下:
其中,prefixSum[i][j]
表示矩阵中 (1,1)
到 (i,j)
所有元素的和,matrix[i][j]
表示矩阵中第 i
行第 j
列的元素。
对于每个询问,我们读取左上角坐标 (x1, y1)
和右下角坐标 (x2, y2)
,然后使用前缀和数组 prefixSum
计算子矩阵中所有数的和:
最后,输出结果 result
。
Q:为什么能这样计算前缀和呢?
可能有很多朋友到这感觉稍微抽象,不能直接的理解。
假设我们有以下 3x3 的整数矩阵:
1 2 3
4 5 6
7 8 9
首先,我们构建一个与输入矩阵大小相同的前缀和数组 prefixSum
:
0 0 0 0
0 1 3 6
0 5 12 21
0 12 27 45
prefixSum
中的每个元素 prefixSum[i][j]
表示原矩阵中从 (1, 1)
到 (i, j)
的子矩阵的和。
例如,prefixSum[2][2]
表示原矩阵中从 (1, 1)
到 (2, 2)
的子矩阵的和,即 1 + 2 + 4 + 5 = 12
。
现在,我们可以使用前缀和数组 prefixSum
来计算任意子矩阵的和。
假设我们想计算从 (1, 1)
到 (2, 3)
的子矩阵的和,即矩阵中的所有元素的和。我们可以使用前缀和数组的性质来计算:
那么
result = prefixSum[2][3] - prefixSum[2][0] - prefixSum[0][3] + prefixSum[0][0]
= 21 - 0 - 0 + 0
= 21
因此,从 (1, 1)
到 (2, 3)
的子矩阵的和为 21。
通过构建和利用前缀和数组,我们可以在常数时间复杂度内计算任意子矩阵的和,而不需要每次都重新遍历矩阵。这在处理大型矩阵和多个询问时非常高效。