前缀和介绍
前缀和是一个数组的某项下标之前(包括此项元素)的所有数组元素的和。分为一维和二维,一维前缀和可以很快速的求序列中某一段的和。而二维前缀和可以快速求一个矩阵中某个子矩阵的和。
作用:快速求出元素组中某段区间的和
下面我们先深入了解前缀和
前缀和其实我们很早之前就了解过的,高中我们求数列的和时,Sn = a1+a2+a3+...an; 此时Sn就是数列的前 n 项和。例 如S5 = a1 + a2 + a3 + a4 + a5; S2 = a1 + a2。可以看出我们完全可以通过 S5-S2 得到 a3+a4+a5 的值,这个过程就和我们做题用到的前缀和思想类似。前缀和数组里保存的就是前 n 项的和。
通过前缀和数组保存前 n 位的和,sum[1]保存的就是 nums 数组中前 1 位的和,也就是 sum[1] = nums[0], sum[2] = nums[0] + nums[1] = sum[1] + nums[1]. 依次类推,所以我们通过前缀和数组可以轻松得到每个区间的和。
求一段区间的和最直接写法:
//一维
#include<bits/stdc++.h>
using namespace std;
int a[100000];
int main(void)
{
int l,r;
cin>>l>>r;
int res=0;
for(int i=l;i<=r;i++)//下标从l到r区间数组的和
res+=a[i];
return 0;
}
//二维
#include<bits/stdc++.h>
using namespace std;
int a[100000][100000];
int main(void)
{
int x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;//左上角(x1,y1)到(x2,y2)矩形区域数组的和
int res=0;
for(int i=x1;i<=x2;i++)//下标从l到r区间数组的和
for(int j=y1;j<=y2;j++)
res+=a[i][j];
return 0;
}
那么我们如何用前缀和得到nums[2]到nums[4]区间的和呢?
sum[3]=sum[2]+nums[2];
sum[4]=sum[3]+nums[3];
sum[5]=sum[4]+nums[4];
sum[5]=sum[2]+nums[2]+nums[3]+nums[4];
sum[5]-sum[2]=nums[2]+nums[3]+nums[4];
由上我们就了解了前缀和的基本思想了,上代码
for (int i = 0; i < nums.length; i++) {
sum[i] = sum[i-1]+nums[i];//求和数组sn=sn-1+an
}
一维前缀和
int nums[N],sum[N];
for(int i=1;i<=n;i++)
cin>>nums[i];//读入数组元素
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]+nums[i];
前缀和数组初始化要从1开始,加和时要用到i-1项,从0开始会导致数组越界,出现a[-1]
二维前缀和
二位前缀和数组S[i][j]代表以(1,1)为左上角,(i,j)为右下角的数组元素的和。
整个矩形=(紫色+绿色)+(紫色+黄色)-红色+蓝色
sum[i][j]=sum[i-1][j]+s[i][j-1]-s[i-1][j-1]+nums[i];
int nums[N][N],sum[N][N];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
sum[i][j]=sum[i-1][j]+s[i][j-1]-s[i-1][j-1]+nums[i];
}
例题1
一种新型的激光炸弹,可以摧毁一个边长为R的正方形内的所有的目标。现在地图上有n(N<=10000)个目标,用整数Xi,Yi(其值在[0,5000])表示目标在地图上的位置,每个目标都有一个价值。激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆破范围,即那个边长为R的正方形的边必须和x,y轴平行。若目标位于爆破正方形的边上,该目标将不会被摧毁。
Input
输入文件的第一行为正整数n和正整数R,接下来的n行每行有3个正整数,分别表示xi,yi,vi
Output
输出文件仅有一个正整数,表示一颗炸弹最多能炸掉地图上总价值为多少的目标(结果不会超过32767)。
Sample
Inputcopy Outputcopy 2 1
0 0 1
1 1 11
#include<bits/stdc++.h>
using namespace std;
int a[5005][5005];
int main(void)
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
int x,y,v;
cin>>x>>y>>v;
a[x+1][y+1]=v;//矩阵整体右移
}
for(int i=1;i<=5001;i++)
{
for(int j=1;j<=5001;j++)
{
a[i][j]=a[i-1][j]+a[i][j-1]-a[i-1][j-1]+a[i][j];
}
}
int ans=0;
for(int i=m;i<=5001;i++)
{
for(int j=m;j<=5001;j++)
{
int t=a[i][j]-a[i-m][j]-a[i][j-m]+a[i-m][j-m];
ans=max(ans,t);//更新答案
}
}
cout<<ans<<endl;
return 0;
}
例题2
Background
小 K 又在做白日梦了。他进入到他的幻想中,发现他打下了一片江山。
Description
小 K 打下的江山一共有 nn 个城市,城市 ii 和城市 i+1i+1 有一条双向高速公路连接,走这条路要耗费时间 a_iai。
小 K 为了关心人民生活,决定定期进行走访。他每一次会从 11 号城市到 nn 号城市并在经过的城市进行访问。其中终点必须为城市 nn。
不仅如此,他还有一个传送器,传送半径为 kk,也就是可以传送到 i-ki−k 和 i+ki+k。如果目标城市编号小于 11 则为 11,大于 nn 则为 nn。
但是他的传送器电量不足,只能传送一次,况且由于一些原因,他想尽量快的完成访问,于是就想问交通部部长您最快的时间是多少。
注意:他可以不访问所有的城市,使用传送器不耗费时间。
Input
两行,第一行 n,kn,k。
第二行 n-1n−1 个整数,第 ii 个表示a_iai。
Output
一个整数,表示答案。
Sample 1
Inputcopy Outputcopy 4 0 1 2 3 6Sample 2
Inputcopy Outputcopy 4 1 1 2 3 3Hint
样例解释 1:
样例 1,2 的图示均为以下图片:
不使用传送器直接走,答案为 66,可以证明这个是最小值。
样例解释 2:
在 33 处使用,传送到 44,答案为 33,可以证明这个是最小值。
数据范围:
对于所有数据,a_i > 0ai>0
直接用前缀和最后一项(s[n-1],总距离)-t(传送器可传送最大距离)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[1000010],s[1000010];
ll t=0;
int main(void)
{
int n,k;
cin>>n>>k;
for(int i=1;i<n;i++)
{
scanf("%lld",&a[i]);
s[i]=s[i-1]+a[i];
}
for(int i=k;i<n;i++)
t=max(t,s[i]-s[i-k]);//传送器传送最大距离
if(k>=n)
cout<<0<<endl;
else
cout<<s[n-1]-t<<endl;
return 0;
}
例题3
给出一个长度为 NN 的数组,进行 Q次查询,查询从第 i个元素开始长度为 l的子段所有元素之和。 例如,1379−1 ,查询第 2 个元素开始长度为 3 的子段和, 1{379}−1 。 3 + 7 + 9 = 19,输出 19。
Input
第 1行:一个数 N , N 为数组的长度( 2≤N≤50000) 。 第 2至 N+1 行:数组的 N个元素。(109≤N[i]≤109) 第 N+2 行: 1个数 Q, Q为查询的数量。 第 N+3 至 N + Q + 2 行:每行 2 个数, i , l ( 1≤i≤N , i+l≤N)
Output
共 Q行,对应 Q 次查询的计算结果。
Data Description
Sample
Inputcopy Outputcopy ["5\n1\n3\n7\n9\n-1\n4\n1 2\n2 2\n3 2\n1 5"] ["4\n10\n16\n19"]
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[50005],m,n,b;
int main(void)
{
int n,m;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>b;
a[i]=a[i-1]+b;
}
cin>>m;
while(m--)
{
int i,l;
cin>>i>>l;
cout<<a[l+i-1]-a[i-1]<<endl;
}
return 0;
}