一维前缀和

一维前缀和

前缀和数组的作用是:将对区间的查询变为对区间端点的查询。
常用于计算数列的第L个元素到第R个元素之和的问题,即数列的区间求和问题

前缀和 —— prefixSum
举例:

假设原数组nums[ ] = {0,5,6,7,8};
可由此数组递推得到前缀和数组prefixSums[ ] = {5,11,18,26};
注意:前缀和数组的下标从1开始存储数值

递推过程如下:
prefixSums[ 0 ] = nums[ 0 ] = 0 ;

prefixSums[ 1 ] = nums[ 0 ] + nums[ 1 ] = prefixSums[ 0 ] + nums[ 1 ] = 5 ;

prefixSums[ 2 ] = nums[ 0 ] + nums[ 1 ] + nums[ 2 ]= prefixSums[ 1 ] + nums[ 2 ] = 11 ;

prefixSums[ 3 ] = nums[ 0 ] + nums[ 1 ] + nums[ 2 ] + nums[ 3 ]= prefixSums[ 2 ] + nums[ 3 ] = 18 ;

prefixSums[ 4 ] = nums[ 0 ] + nums[ 1 ] + nums[ 2 ] + nums[ 3 ] + nums[ 4 ] = prefixSums[ 3 ] + nums[ 4 ] = 26 ;

由以上总结出:
prefixSums[ i ] = prefixSum[ i - 1] + nums[ i ];

重点!!!
可用prefixSum[ R ] - prefixSum[ L - 1 ]计算第L个元素到第R和元素的和

前缀和数组的代码实现:

#include <iostream>

using namespace std;

const int N = 10010;
int nums[N];//原数组
int prefixSums[N];//前缀和数组

int main()
{
    
    
    int n;
    scanf("%d",&n);
    
    //prefixSums[i - 1]的下标从0开始,所以i要从1开始
    for (int i = 1;i <= n;i++)
    {
    
    
        scanf("%d",&nums[i]);
        
        //核心代码
        prefixSums[i] = prefixSums[i - 1] + nums[i];
    }
    for (int i = 1;i <= n;i++) printf("%d ",prefixSums[i]);
    return 0;
}

例题:
原题链接
有 N 个的正整数放到数组 A 里,现在要求你求出并输出这样一个数组:
这个数组的第 i 个数是数组 A 的第一个到数组 A 的第 i 个数的和。
输入
第一行 1 个正整数:N,N 范围在[1, 100]。
第二行 N 个正整数:范围在[1, 10000]。
输出
N 个正整数。
样例输入
6
2 6 1 9 7 3
样例输出
2 8 9 18 25 28

AC代码:

#include <cstdio>
#include <iostream>

using namespace std;

const int N = 110;
int prefixSum[N];//定义前缀和数组
int a[N];//定义原数组

int main()
{
    
    
    int n;
    scanf("%d",&n);
    
    //统一从1开始计数
    for (int i = 1;i <= n;i++)
    {
    
    
        scanf("%d",&a[i]);
        prefixSum[i] = prefixSum[i - 1] + a[i];
    }
    for (int j = 1;j <= n;j++) printf("%d ",prefixSum[j]);
    return 0;
}

例题2
原题链接
题目描述
给一个长度为 n 的数组,求数组 arr 某个区间 [L,R] 之间的元素和为多少。
输入
第一行一个数字 n,表示数组的长度。
第二行包括 n 个整数,每个整数之间用空格隔开,表示数组的数据。
第三行一个数字 m,表示要进行的操作次数。
从第四行开始到 m+3 行,每行包括两个数组 L 和 R,表示操作的数组区间。
输出
一共 m 行,每行一个整数,表示对应的区间元素和。
样例输入 Copy
10
5 2 -3 1 -5 10 150 300 1200 93
4
1 3
2 5
1 8
1 10
样例输出 Copy
4
-5
460
1753
提示
【数据范围】
1 ⩽ m ⩽ n ⩽10 ^ 5
−10 ^ 12 ⩽ ai ⩽10 ^ 12
1 ⩽ R ⩽ L⩽10 ^ 5

#include <iostream>
#include <cstdio>
 
using namespace std;
 
const int N = 100010;
long long a[N];//int 最大值是2147483647,而−10^12⩽ai⩽10^12
 
int main()
{
    
    
    int n;
    scanf("%d",&n);
    for (int i = 1;i <= n;i++) scanf("%lld",&a[i]);
     
    int m;
    scanf("%d",&m);
    while (m--)
    {
    
    
        long long int ans = 0;
        int l,r;
        scanf("%d%d",&l,&r);
        for (int j = l;j <= r;j++) ans += a[j];
        printf("%lld\n",ans);
    }
    return 0;
}

注:以上朴素算法按理说可以AC,但是不知道为什么。。。

在这里插入图片描述
问了ACM的大佬也没看出来,最后我把代码改成了这样。哪位大佬找到了可以A的代码欢迎指正。

过了一会继续更博客。
好家伙原来是代码交错了题号,真是愚蠢他妈给愚蠢开门愚蠢到家了啊。下面是上个例题的前缀和代码及思想

由题意得
ans = a[ l ] + a[ l + 1] + … … + a[ r - 1] + a[ r ]
= ( a[ 0 ] + a[ 1 ] + a[ 2 ] + … … + a[ l - 1] ) + a[ l ] + a[ l + 1] + … … + a[ r - 1] + a[ r ] - ( a[ 0 ] + a[ 1 ] + a[ 2 ] + … … + a[ l - 1] )
观察得
( a[ 0 ] + a[ 1 ] + a[ 2 ] + … … + a[ l - 1] ) + a[ l ] + a[ l + 1] + … … + a[ r - 1] + a[ r ] = prefixSum[ r ];
( a[ 0 ] + a[ 1 ] + a[ 2 ] + … … + a[ l - 1] ) = prefixSum[l - 1];

即前缀和核心代码:
ans = prefixSum[ r ] - prefixSum[l - 1]

#include <cstdio>
#include <iostream>

using namespace std;

const int N = 100010;
long long prefixSum[N];//前缀和数组
long long a[N];//原数组


int main()
{
    
    
    int n;
    scanf("%d",&n);
    
    //从1开始,数组下标就刚好是第几位数
    for (int i = 1;i <= n;i++)
    {
    
    
        scanf("%lld",&a[i]);
        
        //前缀和数组
        prefixSum[i] = prefixSum[i - 1] + a[i];
    }
    
    int m;
    scanf("%d",&m);
    for (int i = 1;i <= m;i++)
    {
    
    
        int l,r;
        long long ans = 0;
        scanf("%d%d",&l,&r);
        ans = prefixSum[r] - prefixSum[l - 1];
        printf("%lld\n",ans);
    }
    
    return 0;
}

当然,相比于上一个朴素算法,前缀和将时间复杂度从O(n ^ 2)级别优化到了O( n )级别

猜你喜欢

转载自blog.csdn.net/smallrain6/article/details/112757186