前缀和2️⃣-二维前缀和
题目链接:**【模板】二维前缀和_牛客题霸_牛客网**
题目描述:
解法:
算法思路:
类比于一维数组的形式,如果我们能处理出来从[0, 0] 位置到 [i, j] 位置这片区域内所有元素的累加和,就可以在O(1) 的时间内,搞定矩阵内任意区域内所有元素的累加和。因此我们接下来仅需完成两步即可:
◦ 第一步:搞出来前缀和矩阵
这里就要用到一维数组里面的拓展知识,我们要在矩阵的最上面和最左边添加上一行和一列0,这样我们就可以省去非常多的边界条件的处理处理后的矩阵就像这样:
这样,我们填写前缀和矩阵数组的时候,下标直接从1开始,能大胆使用i - 1 ,j - 1 位置的值。
注意 dp 表与原数组 matrix 内的元素的映射关系:
❍ 从dp 表到 matrix 矩阵,横纵坐标减一;
❍ 从matrix 矩阵到 dp 表,横纵坐标加一。
前缀和矩阵中 sum[i] [j]的含义,以及如何递推二维前缀和方程
◉ sum[i] [j]的含义:
sum[i] [j] 表示,从下图的红色区域:[0, 0] 位置到 [i, j] 位置这段区域内,所有元素的累加和。对应下图的红色区域:
递推方程:其实这个递推方程非常像我们小学做过求图形面积的题,我们可以将 [0, 0] 位置到 [i, j]位置这段区域分解成下面的部分:
sum[i] [j] = 红 + 蓝 + 绿 + 黄,分析一下这四块区域:
✸ 黄色部分最简单,它就是数组中的 matrix[i ] [j ] (注意坐标的映射关系)
✸ 单独的蓝不好求,因为它不是我们定义的状态表示中的区域,同理,单独的绿也是;
✸ 但是如果是红 + 蓝,正好是我们dp 数组中 sum[i - 1] [j] 的值,美滋滋;
✸ 同理,如果是红 + 绿,正好是我们dp 数组中 sum[i] [j - 1] 的值;
✸ 如果把上面求的三个值加起来,那就是黄 + 红 + 蓝 + 红 + 绿,发现多算了一部分红的面积,因此再单独减去红的面积即可;
✸ 红的面积正好也是符合 dp 数组的定义的,即 sum[i - 1] [j - 1]
综上所述,我们的递推方程就是:
sum[i][j]=sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j -1]+matrix[i][j]
◦ 第二步:使用前缀和矩阵
求[x1,y1]~[x2,y2]内值的和
为A+B+C+D - (A+B) - (A+C) + A
则D = dp[x2] [y2] - dp[x1-1] [y2] - dp[x2] [y1-1] +dp[x1-1] [y1-1]
C++算法代码:
#include <iostream>
using namespace std;
#include <vector>
int main() {
//1.读入数据
int n = 0, m = 0, q = 0;
cin >> n >> m >> q;
vector<vector<int>> arr(n+1, vector<int>(m+1));
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
cin >> arr[i][j];
//2.预处理前缀和矩阵
vector<vector<long long>> dp(n+1, vector<long long>(m+1));
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
dp[i][j] = dp[i - 1][j] + dp[i][j - 1] + arr[i][j] - dp[i - 1][j - 1];
//3.使用前缀和矩阵
int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
while(q--){
cin >> x1 >> y1 >> x2 >> y2;
cout << dp[x2] [y2] - dp[x1-1] [y2] - dp[x2] [y1-1] +dp[x1-1] [y1-1] << endl;
}
}
// 64 位输出请用 printf("%lld")