前缀和

前缀和是一种优化数组区间求和的算法,它可以快速计算出一维或二维数组中某段区间的元素和。一维前缀和通过累加数组元素实现,二维前缀和则涉及到矩阵运算。文章通过示例展示了如何利用前缀和计算特定区间和,并提供了两个实际问题的解决方案,一个是激光炸弹摧毁目标的优化问题,另一个是关于最短访问时间的问题,以及处理查询子数组和的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 前缀和介绍

前缀和是一个数组的某项下标之前(包括此项元素)的所有数组元素的和。分为一维和二维,一维前缀和可以很快速的求序列中某一段的和。而二维前缀和可以快速求一个矩阵中某个子矩阵的和。

作用:快速求出元素组中某段区间的和

下面我们先深入了解前缀和

前缀和其实我们很早之前就了解过的,高中我们求数列的和时,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

InputcopyOutputcopy
2 1
0 0 1
1 1 1
1
#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

InputcopyOutputcopy
4 0
1 2 3
6

Sample 2

InputcopyOutputcopy
4 1
1 2 3
3

Hint

样例解释 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

InputcopyOutputcopy
["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;
 } 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

易哈哈哈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值