赞
踩
目录
定义:sum[i]:是数组0~数组i的和
例:一个数组:1,3,7,5,2
它的前缀和数组为:1,4,11,16,18
前缀和数组:数组中的位置i的值是数组0~i之间的和
作用:可以用于快速求数组区间的和(尤其是多次操作)
例:求数组arr的区间(l,r)的和ans
- //核心代码:
-
- sum[0]=arr[0];
-
- for(int i=1;i<n;i++)
-
- sum[i]=sum[i-1]+arr[i]; //此时sum数组存储的前缀和
-
- ans=sum[r]-sum[l-1]; //(l>0)
-
- ans=sum[r]; //l=0
例:arr[5]={1,3,7,5,2};
求区间: (2,4),(0,3),(3,4)三个区间的区间和
- #include<bits/stdc++.h>
- using namespace std;
- const int n=5;
- int arr[n]={1,3,7,5,2};
- int sum[n];
- void get_sum() //前缀和
- {
- sum[0]=arr[0];
- for(int i=1;i<n;i++)
- sum[i]=sum[i-1]+arr[i];
- }
- int add(int l,int r) //对区间(l,r)求和
- {
- if(l==0) return sum[r]; //如果l=0,说明此时只需要求(0,r)之间的数字和,也就是此时的sum[r]
- return sum[r]-sum[l-1]; //如果l>0,说明求的是(l,r)之间的和,也就是需要将sum[r]减去sum[l-1]
- //因为sum[r]存储的是0~r之间的数字和,sum[l]存储的是0~l之间的数字和;
- }
- int main(int argc, char** argv) { //没有一点技术含量的主函数
- get_sum();
- cout<<add(2,4)<<endl;
- cout<<add(0,3)<<endl;
- cout<<add(3,4)<<endl;
- return 0;
- }

对一个给定的自然数M,求出所有的连续的自然数段(每一段至少有两个数),这些连续的自然数段中的全部数之和为M。
例子:1998+1999+2000+2001+2002 = 100001998+1999+2000+2001+2002=10000,所以从19981998到20022002的一个自然数段为M=10000M=10000的一个解。
包含一个整数的单独一行给出M的值(10≤M≤2,000,000)。
每行两个自然数,给出一个满足条件的连续自然数段中的第一个数和最后一个数,两数之间用一个空格隔开,所有输出行的第一个按从小到大的升序排列,对于给定的输入数据,保证至少有一个解。
输入
10000
输出
18 142 297 328 388 412 1998 2002
此题可以使用多种解法,如暴力枚举、尺取法、前缀和法, 在此使用的是前缀和法。
AC代码:
- #include <iostream>
- #include <bits/stdc++.h>
- using namespace std;
- long long sum[2000001];
- int main(int argc, char** argv) {
- long long n;
- cin>>n;
- for(int i=1;i<=n;i++) //计算前缀和,并存入数组sum中
- sum[i]=sum[i-1]+i;
- for(int l=1;l<=n;l++)//通过标记查找,符合结果的数
- {
- //标记mid=sum[l-1]+n,在sum数组中查找;
- //因为区间(l,r)的区间和n=sum[r]-sum[l-1];此时需要寻找的是r和l-1
- //从0开始遍历数组,遍历的作为左区间l,此时可以通过sum[l-1]+n=sum[r]来寻找右区间r
- auto mid=sum[l-1]+n; //auto关键字可以自动识别变量的类型
- auto r=lower_bound(sum,sum+n+1,mid)-sum;
- //lower_bound函数是在指定区间内查找不小于mid的第一个元素
- if(sum[r]-sum[l-1]==n)//此时sum[a]=sum[i-1]+n
- {
- if(l!=r)
- cout<<l<<" "<<a<<endl;
- }
- }
- return 0;
- }

【题目描述】 给定n个整数a1,a2,…,an,求他们两两相乘再相加的和。
即: S = a1•a2 + a1•a3 + ... + a1•an + a2•a3 + ... + an-2•an-1 + an-2•an + an-1•an
【输入描述】 第一行包含一个整数n,第二行包含n个整数a1,a2,…,an。
【输出描述】输出一个整数S,表示所求的和。使用合适的数据类型进行运算。
【输入样例】 4
1 3 6 9
【输出样例】 117
【评测用例规模与约定】
对于30%的数据,1≤n≤1000,1≤ai≤100。 对于所有评测用例,1 ≤n≤200000,1≤ai≤1000。
思路分析:
从题目中可以看出有些式子都是由一个共同的公因式,将它提到外面可以得到下面的式子:
S = (a1+a2 +...+an-1)•an + (a1+a2 +...+an-2)•an-1 +(a1+a2 +...+an-3)•an-2 + ...+(a1+a2)•a3 + a1•a2
在这个中,我们可以发现,括号里面的数可以使用前缀和来实现。
即:sum[i] = a1+ a2+…+ ai
则上式可以改成:
S = sum[n-1] •an + sum[n-2]•an-1 + sum[n-3]•an-2 + ... + sum[2]•a3 + sum[1]•a2
前缀和只用到sum[1]~sum[n-1]
AC代码:
- #include <iostream>
- using namespace std;
- /* run this program using the console pauser or add your own getch, system("pause") or input loop */
- const int N = 200005;
- int a[N];
- long long sum[N];//前缀和数组,因为去和数字有点大,记得开long long
- int main(int argc, char** argv) {
- int n;
- cin>>n;
- sum[1]=0;//将sum[0]置为0
- for(int i=1;i<=n;i++)
- {
- cin>>a[i];
- sum[i]=sum[i-1]+a[i];//计算前缀和
- }
- long long ans=0;//存储答案
- for(int i=1;i<=n;i++)
- {
- ans+=sum[i]*a[i+1];//当i等于n时,sum[n]*a[n+1]=0(a[n+1]=0),所以没有用到sum[n]
- }
- cout<<ans;
- return 0;
- }

一维前缀和只能用于一维的数据结构求和(如数组),二维前缀和则是在二维数据结构上求和(如矩阵)。
定义:求矩阵g[0,0]到[i,j]的值的和
sum[i][j]:是矩阵g[0][0]到g[i][j]的二维前缀和(存放二维前缀和)
g[i][j]是该矩阵i行j列的值(存放原矩阵)性质:sum[i][j]=g[i][j]+sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1];
作用:求矩阵区间:[x1][y1]~[x2][y2]的区间和ans(尤其是多次询问操作)
核心代码:
- ans=sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1];
- //此公式可以进行画图理解
- /* ps:如果i=0,ans=sum[x2][y2]-sum[x1-1][y2];
- 如果j=0, ans=sum[x2][y2]-sum[x2][y1-1];
- 如果i=0,j=0, ans=sum[x2][y2];
- ps:当i=0/j=0时,可以将其看成一维数组进行前缀和进行计算
- sum[0][j]=sum[0][j-1]+g[0][j];
- sum[i][0]=sum[i-1][0]+g[i][0]
- */
例:
矩阵: 1,5,6,8
9,6,7,3
5,3,2,4
1、求出二维前缀和sum
2、求区间([1][1]~[2][2])的和
答案:
二维前缀和sum:
1 6 12 20
10 21 34 45
15 29 44 59
[1][1]~[2][2]区间和为:18
AC代码:
- #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 j=1;j<=m;j++) sum[0][j]=sum[0][j-1]+g[0][j]; //第一行
- for(int i=1;i<=n;i++) sum[i][0]=sum[i-1][0]+g[i][0]; //第一列
- for(int i=1;i<n;i++) //其它
- for(int j=1;j<m;j++)
- sum[i][j]=sum[i-1][j]+sum[i][j-1]+g[i][j]-sum[i-1][j-1];
- }
- int get_sum(int x1,int y1,int x2,int y2) //求区间和
- {
- if(x1==0&&y1==0) return sum[x2][y2]; //求的就是sum[x2][y2]的前缀和
- if(x1==0) return sum[x2][y2]-sum[x1-1][y2];
- if(y1==0) return sum[x2][y2]-sum[x2][y1-1];
- return sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1];
- }
- int main(int argc, char** argv) {
- pre_sum();
- cout<<"二维前缀和sum:";
- for(int i=0;i<n;i++)
- {
- cout<<endl;
- for(int j=0;j<m;j++)
- cout<<sum[i][j]<<" ";
- }
- cout<<endl;
- cout<<"区间和为:" ;
- cout<<get_sum(1,1,2,2)<<" ";
- return 0;
- }
-
-

为了更好的备战 NOIP2013,电脑组的几个女孩子 LYQ,ZSC,ZHQ 认为,我们不光需要机房,我们还需要运动,于是就决定找校长申请一块电脑组的课余运动场地,听说她们都是电脑组的高手,校长没有马上答应他们,而是先给她们出了一道数学题,并且告诉她们:你们能获得的运动场地的面积就是你们能找到的这个最大的数字。
校长先给他们一个n×n 矩阵。要求矩阵中最大加权矩形,即矩阵的每一个元素都有一权值,权值定义在整数集上。从中找一矩形,矩形大小无限制,是其中包含的所有元素的和最大 。矩阵的每个元素属于 [-127,127][−127,127] ,例如
- 0 –2 –7 0
- 9 2 –6 2
- -4 1 –4 1
- -1 8 0 –2
在左下角:
- 9 2
- -4 1
- -1 8
和为 15。
几个女孩子有点犯难了,于是就找到了电脑组精打细算的 HZH,TZY 小朋友帮忙计算,但是遗憾的是他们的答案都不一样,涉及土地的事情我们可不能含糊,你能帮忙计算出校长所给的矩形中加权和最大的矩形吗?
第一行:n,接下来是 n 行 n 列的矩阵。
最大矩形(子矩阵)的和。
输入
4 0 -2 -7 0 9 2 -6 2 -4 1 -4 1 -1 8 0 -2
输出
15
1≤n≤120
- #include <iostream>
- using namespace std;
- int n;
- int a[137][137]; //原矩阵
- int sum[137][137]; //存放二维前缀和
- int main(int argc, char** argv) {
- int n;
- cin>>n;
- for(int i=1;i<=n;i++)
- {
- for(int j=1;j<=n;j++)
- {
- cin>>a[i][j];
- sum[i][j]=sum[i-1][j]+sum[i][j-1]+a[i][j]-sum[i-1][j-1]; //计算二维前缀和,并存入sum数组
- }
- }
- int mx=-9999999999; //寻找最大值,先定义一个最小值
- for(int x1=1;x1<=n;x1++) //对矩阵进行遍历
- {
- for(int y1=1;y1<=n;y1++)
- {
- for(int x2=1;x2<=n;x2++)
- {
- for(int y2=1;y2<=n;y2++)
- {
- if(x2<x1||y2<y1) //必须符合x2>x1,y2>y1才可以
- continue;
- mx=max(mx,sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1]); //寻找子区间的最大值
- }
- }
- }
- }
- cout<<mx;
- return 0;
- }

掌握了基本前缀和的算法,你一定非常想要使用它来获取AC的快感吧。下面贴出了洛谷平台的前缀和题单,你确定不来试试吗?
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。