问题如下:
输入一个 n 行 m 列的整数矩阵,再输入 q 个询问,每个询问包含四个整数 x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。
对于每个询问输出子矩阵中所有数的和。
输入格式 第一行包含三个整数 n,m,q。
接下来 n 行,每行包含 m 个整数,表示整数矩阵。
接下来 q 行,每行包含四个整数 x1,y1,x2,y2,表示一组询问。
输出格式 共 q 行,每行输出一个询问的结果。
数据范围 1≤n,m≤1000, 1≤q≤200000, 1≤x1≤x2≤n, 1≤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
这个问题可以分成两步来解决。首先让我们观察目标在图形中的表示。
矩阵中的每个格子为对应位置的元素,而我们的目标就是统计出绿色部分矩阵的元素之和。
通过观察,我们发现这个值其实可通过若干子矩阵与元素的加减获得。
定义
s
[
x
i
,
y
i
]
表示以
(
x
i
,
y
i
)
为右下角,以原矩阵左上角为左上角的子矩阵元素之和,即上图黄色部分。
s[x_i,y_i]表示以(x_i,y_i)为右下角,以原矩阵左上角为左上角的子矩阵元素之和,即上图黄色部分。
s[xi,yi]表示以(xi,yi)为右下角,以原矩阵左上角为左上角的子矩阵元素之和,即上图黄色部分。
由此,对于原来的绿色部分子矩阵之和,我们有:
g
r
e
e
n
=
s
[
x
2
,
y
2
]
−
s
[
x
1
−
1
,
y
2
]
−
s
[
x
2
,
y
1
−
1
]
+
s
[
x
1
−
1
,
y
1
−
1
]
green=s[x_2,y_2]-s[x_1-1,y_2]-s[x_2,y_1-1]+s[x_1-1,y_1-1]
green=s[x2,y2]−s[x1−1,y2]−s[x2,y1−1]+s[x1−1,y1−1]
红色部分代表只减了一次,灰色部分代表
s
[
x
1
−
1
,
y
2
]
与
s
[
x
2
,
y
−
1
]
重叠的部分,被减了两次,因此需要加回来
s[x_1-1 ,y_2]与s[x_2,y_-1]重叠的部分,被减了两次,因此需要加回来
s[x1−1,y2]与s[x2,y−1]重叠的部分,被减了两次,因此需要加回来。
子矩阵之和的问题解决了,那么如何计算
s
[
i
,
j
]
s[i,j]
s[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[i,j]=s[i−1,j]+s[i,j−1]−s[i−1,j−1]+a[i,j]
紫色部分为我们要求的 s [ i , j ] s[i,j] s[i,j],红色部分只加了一次,灰色部分为 s [ i − 1 , j ] + s [ i , j − 1 ] s[i-1,j]+s[i,j-1] s[i−1,j]+s[i,j−1]的重叠部分,即 s [ i − 1 , j − 1 ] s[i-1,j-1] s[i−1,j−1], 加了两次所以要减去一次。a[i,j]为对应位置的元素取值,对应黄色部分。
最终我们得到两条式子:
g
r
e
e
n
=
s
[
x
2
,
y
2
]
−
s
[
x
1
−
1
,
y
2
]
−
s
[
x
2
,
y
1
−
1
]
+
s
[
x
1
−
1
,
y
1
−
1
]
green=s[x_2,y_2]-s[x_1-1,y_2]-s[x_2,y_1-1]+s[x_1-1,y_1-1]
green=s[x2,y2]−s[x1−1,y2]−s[x2,y1−1]+s[x1−1,y1−1]
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]
代码如下(c++版):
#include<iostream>
using namespace std;
/*
第一步,在最顶端初始化所有要用到的变量与常量
*/
const int N = 1010;
int n, m, q;
int a[N][N], s[N][N];
int main() {
//输出的格式,记住了
scanf("%d%d%d", & n, &m, &q);
//输入n行m列的数据
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
scanf("%d", &a[i][j]);
s[i][j] = s[i][j - 1] + s[i - 1][j] - s[i - 1][j - 1] + a[i][j]; // 求前缀和
}
//处理查询
int x1, x2, y1, y2;
while (q--) {
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
printf("%d\n", (s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]));
}
return 0;
}