Acwing796 子矩阵的和

问题如下:

输入一个 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[x11,y2]s[x2,y11]+s[x11,y11]
红色部分代表只减了一次,灰色部分代表 s [ x 1 − 1 , y 2 ] 与 s [ x 2 , y − 1 ] 重叠的部分,被减了两次,因此需要加回来 s[x_1-1 ,y_2]与s[x_2,y_-1]重叠的部分,被减了两次,因此需要加回来 s[x11,y2]s[x2,y1]重叠的部分,被减了两次,因此需要加回来

 子矩阵之和的问题解决了,那么如何计算 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[i1,j]+s[i,j1]s[i1,j1]+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[i1,j]+s[i,j1]的重叠部分,即 s [ i − 1 , j − 1 ] s[i-1,j-1] s[i1,j1], 加了两次所以要减去一次。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[x11,y2]s[x2,y11]+s[x11,y11]
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[i1,j]+s[i,j1]s[i1,j1]+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;
		


 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值