0
点赞
收藏
分享

微信扫一扫

前缀和(二维)

小_北_爸 2022-02-15 阅读 73

前言

前面介绍了一维前缀和,二维前缀和是一维前缀和的在线升级,一维前缀和体现在数组,而二位前缀和体现在矩阵上。


一、什么是二维前缀和?

基于立在一维前缀和的基础上,现在所求是矩阵内一个任意的子矩阵的数的和,这样的问题我们就可以用二维前缀和进行求解。

二、二维前缀和讲解

引入

对于一个矩阵
例如:定义一个矩阵g[n][m]

const int n=3,m=4;
int g[n][m]={{1,5,6,8},{9,6,7,3},{5,3,2,4}};
g[n][m]
1   5   6   8
9   6   7   3
5   3   2   4
 (1) 求矩阵(1,1)到(2,2)的和
 (2) 求矩阵(0,1)到(1,3)的和

我们最容易想到的就是暴力枚举法
即:
问题(1) 6+7+3+2=18
问题(2) 5+6+8+6+7+3=35
其复杂度是O(nm) 如果矩阵短小还好 但当数据过大时这种办法就有些不时限了,所以我们引入二维的前缀和是其复杂度降低。

思路

如果我们将这个3*4的矩阵看成一个大长方形
对于问题(1)我们可以有另一种解法
在这里插入图片描述
<<凑活看吧第一次插入图片还没搞懂>>
(阴影为所求)
解法就是:所求阴影=大长方形-(1)-(2)+(3);
结合g[n][m]矩阵,我们进一步分析
找出矩阵g[1],g[2],g[3]

矩阵g[1]                  
1   5   6   
9   6   7   
5   3   2   
矩阵g[2]     矩阵g[3]
1            1   5   6   
9 
5
问题(1)  就可解为:g[1]-g[2]-g[3]+1
现在的问题的就是该如何求矩阵的和了
我们此时引入二维前缀和sum[i][j]
sum[i][j]就是从(0,0)到(i,j)的矩阵和

这样就更好解了
问题(1)=sum[3][3]-sum[2][0]-sum[0][2]+sum[0][0];
由g[n][m] 可以求得sum[n][m]

sum[n][m]
1   6   12   20
10  21  34   45
15  29  44   59

问题(1)=44-15-12+1=18=6+7+3+2

将思维扩大,眼界放开
由对问题(1)的求解可以推断
(设sum[x1,y1][x2,y2]等同
从(x1,y1)到(x2,y2)的和)
sum[x1,y1][x2,y2]
=sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1]

代码实现如下(示例):

#include<bits/stdc++.h>
using namespace std;
const int n=3,m=4;
int g[n][m]={{1,5,6,8},{9,6,7,3},{5,3,2,4}};
int sum[n][m];
void pre_sum()   //预处理生成二维前缀和
{
	sum[0][0]=g[0][0]; //第一个
	for(int i=1;i<n;i++)
		sum[i][0]=sum[i-1][0]+g[i][0];  //第一列
	for(int j=1;j<m;j++)
		sum[0][j]=sum[0][j-1]+g[0][j];  //第一行
	for(int i=1;i<n;i++)
		for(int j=1;j<m;j++)
	sum[i][j]=g[i][j]+sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1];
}
int get_sum(int x1,int y1,int x2,int y2)
{
	if(!x1&&!y1)  return sum[x2][y2];
	if(!x1)    return sum[x2][y2]-sum[x2][y1-1];
	if(!y1)    return sum[x2][y2]-sum[x1-1][y2];
	return sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1];
}
int main()
{
	pre_sum();
	cout<<get_sum(1,1,2,2)<<""<<endl<<get_sum(0,1,1,3);
	return 0;
}

解释

由于sum[][]是二维数组,
所以要先规定边界,再预处理sum[][]数组

void pre_sum()   //预处理生成二维前缀和
{
	sum[0][0]=g[0][0]; //第一个
	for(int i=1;i<n;i++)
		sum[i][0]=sum[i-1][0]+g[i][0];  //第一列
	for(int j=1;j<m;j++)
		sum[0][j]=sum[0][j-1]+g[0][j];  //第一行
	for(int i=1;i<n;i++)
		for(int j=1;j<m;j++)
	sum[i][j]=g[i][j]+sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1];
}

首先前sum[0][0]=g[0][0]肯定没问题吧
下面的两个for语句就是先行规定了sum的边界

sum规定完边界
1   6   12   20
10
15

规定二维前缀和的边界的方法和一维前缀和相同
然后根据边界去求sum[i][j]就相当简单了

for(int i=1;i<n;i++)
	for(int j=1;j<m;j++)
		sum[i][j]=g[i][j]+sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1];

处理自定义函数get_sum()时先return了三种特殊情况
再普遍规律return也很容易理解,就不多说了(就是懒)


总结

二维前缀和和一维前缀和都不是很难理解,想象着图将图,用图去理解可能会更加容易一些。

举报

相关推荐

0 条评论