什么是前缀和?
假如有一个数组
a[0]、a[1]、a[2]、a[3]、...、a[n]
令
s[i]=a[0]+a[1]+...+a[i]//数组前i项和=>区间[0,i]之间的所有和=>i的前缀和
此时的s就是数组的一维前缀和
前缀和是一种比较重要的预处理,它能大大的降低查询的时间复杂度
我们可以通过前缀和来求出任意区间的求和值,比如我们想求出[5,10]区间内的求和值
即s[10]-s[4]=[5,10]
现在有一道前缀和的题,我们来看看。
这题能如果要暴力做的话时间复杂度会很高,所以我们需要用二维前缀和和一些内存优化去做。
这就是二维数组中的一个点的前缀和:
而我们要计算(x,y)点的前缀和需要
s[x][y]=s[x-1][y]+s[x][y-1]-s[x][y]((x,y-1)和(x,y-1)的前缀和有重叠部分,所以需要加上去(x-1,y-1)的前缀和)
而如果我们想计算这个大图中红色的面积
我们可以通过二维前缀和去求
S红=s[x][y]-s[x-R-1,y]-s[x,y-R-1]+s[x-R-1][y-R-1](包括红色面积的周长)
下面是c++的代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int M=5010;
int a[M][M];
int main()
{
int N,R,n,m,res=0;
cin>>N>>R;
n=R;m=R;//可能会被坑,因为有可能激光炸弹的范围比整个矩阵的面积还大,所以需要初始化一下
for(int i=0,x,y,w;i<N;i++)
{
cin>>x>>y>>w;
x++;y++;//把坐标+1的话比较好处理,不用判断范围溢出
n=max(x,n);m=max(y,m);//求矩阵的最大长和宽
a[x][y]+=w;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[i][j]+=(a[i-1][j]+a[i][j-1]-a[i-1][j-1]);//计算二维前缀和
for(int i=R;i<=n;i++)//最坏遍历时间复杂度是o(n^2)
for(int j=R;j<=m;j++)
res=max(res,a[i][j]-a[i-R][j]-a[i][j-R]+a[i-R][j-R]);//找出轰炸的最大价值
cout <<res<< endl;
return 0;
}
这里需要注意的一个地方是,因为所以我们找出轰炸的最大价值时上面的求法其实是求红色面积边长减去1的面积最大价值(这个包含右方和下方的边,处理特殊情况,像题目中给出的输入输出样例就比较特殊),如果想真正求出红色面积所包含的价值(包括边),需要a[i][j]-a[i-R-1][j]-a[i][j-R-1]+a[i-R-1][j-R-1]。