学习二维前缀和之前,大家可以先去了解一下一维前缀和。
https://blog.youkuaiyun.com/qq_63944516/article/details/123495969
算法介绍
二维前缀和是由一维前缀和推演得到的,在一维前缀和中,我们已经知道了多次查询使用前缀和算法会减少运算次数,从而达到了减少运算时间的目的。
二维前缀和的运算的减少
二维前缀和比一维前缀的预处理更为复杂,每个sum[][]需要3次加法、1次减法运算。经过预处理后的查询需要1次加法、2次减法运算。
不过相较于暴力加法,在查询次数较多,数据个数较多的情况下,前缀和依旧占据时间优势。
具体做法
首先做一个预处理,定义一个sum[][]数组,sum[i][j]代表数组中前i行、前j列所代表的矩阵元素之和。
当我们要计算sum[i][j]时,黄色区域的sum[][]已经全部求出。
此时可令sum[i][j]=num[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]。
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
sum[i][j]=num[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
同理一维前缀和,在开辟sum[][]数组时,要扩大一些。经过以上几步,我们就完成了二维前缀和的预处理。
输出查询
当我们要查询x1 y1,x2 y2所代表的子矩阵的和时。
我们可以知道sum[x2][y2]代表的是以x2为行,y2为列的矩阵所有元素之和。
此时,我们可以知道sum=sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][x2-1] ,仿照二维前缀和处理即可。
int x1,y1,x2,y2;
scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
printf("%d\n",sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1]);
题目链接
https://www.acwing.com/problem/content/798/
题目介绍
输入一个 nn 行 mm 列的整数矩阵,再输入 qq 个询问,每个询问包含四个整数 x1,y1,x2,y2x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。
对于每个询问输出子矩阵中所有数的和。
输入格式
第一行包含三个整数 n,m,qn,m,q。
接下来 nn 行,每行包含 mm 个整数,表示整数矩阵。
接下来 qq 行,每行包含四个整数 x1,y1,x2,y2x1,y1,x2,y2,表示一组询问。
输出格式
共 qq 行,每行输出一个询问的结果。
数据范围
1≤n,m≤10001≤n,m≤1000
1≤q≤2000001≤q≤200000
1≤x1≤x2≤n1≤x1≤x2≤n
1≤y1≤y2≤m1≤y1≤y2≤m
−1000≤矩阵内元素的值≤1000
输入样例:
3 4 3
1 7 2 4
3 6 2 8
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4
输出样例:
17
27
21
代码样例
#include<stdio.h>
const int N=1010;
int num[N][N];
int sum[N][N];
int main()
{
int n,m,p;
scanf("%d %d %d",&n,&m,&p);
//原数组
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
scanf("%d",&num[i][j]);
}
// 处理前缀和数组
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+num[i][j];
}
while(p--)
{
int x1,y1,x2,y2;
scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
printf("%d\n",sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1]);
}
return 0;
}