前缀和(一)
写在前面
前缀和极其有用,用来优化算法复杂度,有的时候甚至可以优化掉次方级别的复杂度(因为很多时候我们使用循环就意味着会做很多的重复计算),用一句话总结其本质思想就是,想办法把我们之前算出的能够重复使用的数据存下来,用的时候从里面拿出来,当然存的过程就是耗费空间 的过程,所以我觉得这种思想也算是一种空间换取时间吧!
其实也就是容斥原理的运用!(附上百度百科容斥原理:在计数时,必须注意没有重复,没有遗漏。为了使重叠部分不被重复计算,人们研究出一种新的计数方法,这种方法的基本思想是:先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复,这种计数的方法称为容斥原理)
前缀和
顾名思义,前缀和就是前面 i 个数的总和。
应用场景:给出一串长度为n的数列a1,a2,a3…an,再给出m个询问,每次询问给出L,R两个数,要求给出区间[L,R]里的数的和,如果对于这m次询问,我们每一次都遍历一次,再计算出答案,固然没错,但是数据量大的时候就又可能超时,这时候我们就引入了前缀和的方法来降低时间复杂度。
原理:先计算出前面 i 个数的总和
a[0]=0;
for(int i=1;i<=n;i++)a[i]+=a[i-1];
然后每一次就计算a[R]-a[L-1]即可,原理很好理解。
栗子:
数列:【1,2,3,4,5,6,7,8, 9】
1的前缀和:1 = 1
2的前缀和:1+2 = 3
3的前缀和:1+2+3 = 6
4的前缀和:1+2+3+4 = 10
5的前缀和:1+2+3+4+5 = 15
6的前缀和:1+2+3+4+5+6 = 21
7的前缀和:1+2+3+4+5+6+7 = 28
8的前缀和:1+2+3+4+5+6+7+8 = 36
9的前缀和:1+2+3+4+5+6+7+8+9 = 45
求和如果按一般的两层for循环来写:就是
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= i; ++j) //从第一个到自己
sum[i] += arr[i];
复杂度是N^2的,我们用前缀和的思想来写,可以写出O(N)的算法:
for (int i = 1; i <= n; ++i) {
sum[i] = sum[i - 1] +a[i];
}
也就容易得出求[l,r]的区间和的公式:sum[r]-sum[l-1]。
当然我们实际遇到的相关问题不会这么简单了。
二维数组前缀和
与前面同理,画图理解之后容易得到
S[ i , j ] = S[ i- 1 ,j ] + S[i , j-1 ] - S[i - 1 , j-1 ] + A[ i , j ]
假如我想求a24的前缀和,我得先加上a13的前缀和,再加上a23的前缀和,然后这个时候我们发现实际上a13这个部分我们加了两遍,所以我们需要再减去一遍a13.
就介绍到这里,我会在之后的博文里列举一些题目来感受一下它的魅力!!!
今日推歌
----《凄美地》
嘿 等我找到你
试探你眼睛
心无旁骛地 相拥
那是我 仅有的温柔也是我爱你的原因
在这凄美地
在这之前 别说再见
我已再经不起离别
在这之前 别说再见
我已经开始了想念
在这之前 别说再见
请帮我停住这时间