矩阵前缀和:常数时间求一子矩阵的加和

关于一维数组的前缀和【差分与前缀和,数组的常见预处理技巧

一维数组的前缀和实现了常数时间内求取一个区间的加和,同样地,二维数组的前缀和也是在常数时间内求取某个区间的所有元素加和

如果有一维数组a(下标从1开始),他的前缀和数组为sum(sum[0] = 0)

我们轻易的知道:区间[l, r]的元素加和 = sum[r]-sum[l-1]

同样的,一个矩阵的子矩阵,假设有 r 行 c 列,那么其实就是在原来的 r 个一维数组的前缀和数组的某个区间的加和:

在这里插入图片描述

观察可以发现,这些结果,是某两列的子区间元素的加和,换句话说,还是一维数组(某列)的子区间的元素加和

我们通过对 每一行的前缀和数组组成的矩阵的每一列做列方向的前缀和 ,可以有效的节省上述计算,用常数时间完成一个子矩阵的加和查询

注意

行方向的前缀和和列方向的,一定得分开做,因为列方向的前缀和是基于行前缀和数组组成的矩阵的每一列的

原矩阵最好是从1,1下标开始,方便做前缀和的边界情况

查询
那么对列方向做前缀和,之后得到的矩阵前缀和如何查询呢?假设得到的矩阵前缀和(也是一个矩阵)为a

原矩阵的 x1, y1 下标为左上角,x2, y2 下标为右下角,组成的子矩阵的加和,为

(a[x2][y2]-a[x1-1][y2]) - (a[x2][y1-1]-a[x1-1][y1-1]);

打开括号

a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1];

图解查询
就有点类似求集合的样子
a[i][j] 的值,表示矩阵左上角到 i, j 下标的所有元素加和
在这里插入图片描述
在这里插入图片描述

代码

在这里插入图片描述

#include <bits/stdc++.h>

using namespace std;

int a[1509][1509];
int m, n, x1, y1, x2, y2;

int kksum(int x1, int y1, int x2, int y2)
{
	return a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1];
}

int main()
{	
	cin>>m>>n;
	for(int i=0; i<=m; i++) a[i][0]=0;
	for(int i=0; i<=n; i++) a[0][i]=0;
	// 计算每一行前缀和
	for(int i=1; i<=m; i++)
		for(int j=1; j<=n; j++) 
			cin>>a[i][j], a[i][j]+=a[i][j-1]; 
	// 计算每一列前缀和,一定要和行分开算
	for(int i=1; i<=m; i++)
		for(int j=1; j<=n; j++)
			a[i][j] += a[i-1][j];
	// 输出前缀和矩阵
	cout<<endl;
	for(int i=1; i<=m; i++)
	{
		for(int j=1; j<=n; j++)
			cout<<a[i][j]<<" ";
		cout<<endl;
	}	
	// 验证求和
	cout<<endl;
	while(1)
	{
		cin>>x1>>y1>>x2>>y2;
		cout<<"sum of range is "<<kksum(x1, y1, x2, y2)<<endl;
	}
	
	return 0;
}

/*
2 3 
1 2 3
4 5 6
*/
发布了238 篇原创文章 · 获赞 7 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44176696/article/details/104988020
今日推荐