前缀和矩阵(Prefix Sum Matrix)是一种预处理技术,用于快速计算二维矩阵中任意子矩阵的元素和。其核心思想是通过提前计算并存储每个位置左上角所有元素的和,将子矩阵和的查询时间从暴力计算的 (O(mn)) 优化到 (O(1))。以下是构建前缀和矩阵的详细步骤和示例:
文章目录
-
-
- [1. 定义原矩阵与前缀和矩阵](#1. 定义原矩阵与前缀和矩阵)
- [2. 构建前缀和矩阵的步骤](#2. 构建前缀和矩阵的步骤)
-
- [步骤 1:初始化前缀和矩阵](#步骤 1:初始化前缀和矩阵)
- [步骤 2:递推公式](#步骤 2:递推公式)
- [3. 构建示例](#3. 构建示例)
- [4. 查询子矩阵和](#4. 查询子矩阵和)
- [5. 代码实现(C++)](#5. 代码实现(C++))
- 关键点总结
-
1. 定义原矩阵与前缀和矩阵
- 原矩阵 :一个
m X n
的二维数组matrix
,元素为整数或浮点数。 - 前缀和矩阵 :一个与
matrix
同尺寸的二维数组prefix
,其中prefix[i][j]
表示从左上角(0,0)
到(i,j)
形成的子矩阵所有元素之和。
2. 构建前缀和矩阵的步骤
步骤 1:初始化前缀和矩阵
- 创建一个与原矩阵大小相同的二维数组
prefix
。 - 通常将
prefix
的索引从(0,0)
开始(与matrix
对齐)。
步骤 2:递推公式
-
边界条件:
-
当
i=0
且j=0
时:markdownprefix[0][0] = matrix[0][0]
-
当
i=0
时(第一行):markdownprefix[0][j] = prefix[0][j-1] + matrix[0][j]
-
当
j=0
时(第一列):markdownprefix[i][0] = prefix[i-1][0] + matrix[i][0]
-
-
一般情况 (
i>0
且j>0
):markdownprefix[i][j] = matrix[i][j] + prefix[i-1][j] + prefix[i][j-1] - prefix[i-1][j-1]
解释 :
当前元素的值
matrix[i][j]
,加上上方子矩阵的和prefix[i-1][j]
,加上左方子矩阵的和prefix[i][j-1]
,再减去重复计算的左上角子矩阵prefix[i-1][j-1]
。
3. 构建示例
假设原矩阵 matrix
如下:
markdown
[1 2 3]
[4 5 6]
[7 8 9]
逐行构建前缀和矩阵 prefix
:
-
初始化
prefix[0][0]
:markdownprefix[0][0] = matrix[0][0] = 1
-
第一行(
i=0
):markdownprefix[0][1] &= prefix[0][0] + matrix[0][1] = 1 + 2 = 3 prefix[0][2] &= prefix[0][1] + matrix[0][2] = 3 + 3 = 6
-
第一列(
j=0
):markdownprefix[1][0] &= prefix[0][0] + matrix[1][0] = 1 + 4 = 5 prefix[2][0] &= prefix[1][0] + matrix[2][0] = 5 + 7 = 12
-
一般位置(
i>0
且j>0
):-
prefix[1][1]
:markdown5 + 5 + 3 - 1 = 12
-
prefix[1][2]
:markdown6 + 12 + 6 - 3 = 21
-
prefix[2][1]
:markdown8 + 12 + 12 - 5 = 27
-
prefix[2][2]
:markdown9 + 27 + 21 - 12 = 45
-
最终前缀和矩阵:
markdown
[1 3 6 ]
[5 12 21]
[12 27 45]
4. 查询子矩阵和
构建前缀和矩阵后,计算子矩阵 (x1, y1)
到 (x2, y2)
的和公式为:
markdown
Sum = prefix[x2][y2] - prefix[x1-1][y2] - prefix[x2][y1-1] + prefix[x1-1][y1-1]
示例 :
计算子矩阵 (1,1)
到 (2,2)
(即原矩阵中的 5,6,8,9
)的和:
markdown
Sum = 45 - 6 - 12 + 1 = 28
5. 代码实现(C++)
cpp
#include <vector>
using namespace std;
vector<vector<int>> buildPrefixSum(vector<vector<int>>& matrix) {
int m = matrix.size(), n = matrix[0].size();
vector<vector<int>> prefix(m, vector<int>(n, 0));
// 初始化第一行和第一列
prefix[0][0] = matrix[0][0];
for (int j = 1; j < n; j++)
prefix[0][j] = prefix[0][j-1] + matrix[0][j];
for (int i = 1; i < m; i++)
prefix[i][0] = prefix[i-1][0] + matrix[i][0];
// 填充其他位置
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
prefix[i][j] = matrix[i][j]
+ prefix[i-1][j]
+ prefix[i][j-1]
- prefix[i-1][j-1];
}
}
return prefix;
}
关键点总结
- 时间复杂度:构建前缀和矩阵需 (O(mn)),查询子矩阵和仅需 (O(1))。
- 适用场景:频繁查询子矩阵和的场景(如动态规划、图像处理)。
- 边界处理 :注意索引从
0
开始,避免越界访问。