题目原文
题目描述
输入一个
n
n
n 行
m
m
m 列的整数矩阵,再输入
q
q
q 个询问,每个询问包含四个整数
x
1
,
y
1
,
x
2
,
y
2
,
x1,y1,x2,y2,
x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。
对于每个询问输出子矩阵中所有数的和。
输入格式
第一行包含三个整数
n
,
m
,
q
n,m,q
n,m,q。
接下来 n n n 行,每行包含 m m m 个整数,表示整数矩阵。
接下来 q q q 行,每行包含四个整数 x 1 , y 1 , x 2 , y 2 , x1,y1,x2,y2, x1,y1,x2,y2,表示一组询问。
输出格式
共
q
q
q 行,每行输出一个询问的结果。
数据范围
1
≤
n
,
m
≤
1000
,
1≤n,m≤1000,
1≤n,m≤1000,
1
≤
q
≤
200000
,
1≤q≤200000,
1≤q≤200000,
1
≤
x
1
≤
x
2
≤
n
,
1≤x1≤x2≤n,
1≤x1≤x2≤n,
1
≤
y
1
≤
y
2
≤
m
,
1≤y1≤y2≤m,
1≤y1≤y2≤m,
−
1000
≤
矩阵内元素的值
≤
1000
−1000≤矩阵内元素的值≤1000
−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
解题思路
思路类似于前缀和,见 ACWing 795. 前缀和(C++),即将其由一维扩展到二维。
二维前缀和的每项可由公式求得:
s [ i ] [ j ] = s [ i − 1 ] [ j ] + s [ i ] [ j − 1 ] − s [ i − 1 ] [ j − 1 ] + a [ i ] [ j ] s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j] s[i][j]=s[i−1][j]+s[i][j−1]−s[i−1][j−1]+a[i][j]
对于每次的询问操作,子矩阵之和 s u m sum sum 同样可直接用公式求得:
s u m = s [ x 2 ] [ y 2 ] − s [ x 1 − 1 ] [ y 2 ] − s [ x 2 ] [ y 1 − 1 ] + s [ x 1 − 1 ] [ y 1 − 1 ] sum = s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1] sum=s[x2][y2]−s[x1−1][y2]−s[x2][y1−1]+s[x1−1][y1−1]
将矩阵存储至二维数组中时下标从1开始,这样做有两点好处:
- 使得在进行求子矩阵和的操作过程中行列数与数组中行列数完全对应;
- 注意到求解过程中出现 x 1 − 1 , y 1 − 1 x1 - 1,y1 - 1 x1−1,y1−1 等数,若是从 0 0 0 开始,则为避免数组越界(出现 s [ − 1 ] s[-1] s[−1] 等值),对于这些情况需要重新讨论,增加了程序复杂程度。
代码
#include <iostream>
using namespace std;
const int N = 1010;
int a[N][N], s[N][N];
int main()
{
int n, m, q;
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; i ++ )
{
for (int j = 1; j <= m; j ++ )
{
scanf("%d", &a[i][j]);
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
}
}
while (q -- )
{
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
int sum = s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1];
printf("%d\n", sum);
}
return 0;
}