青少年编程知识记录 codecoming

【算法】前缀和与差分(1)一维数组前缀和

一、定义

前缀和:是指某序列的前n项和。可以理解成数学上上的数列的前n项和。

差分:是前缀和的逆运算。



二、前缀和的分类

可以分成一维数组的前缀和和二维 数组的前缀和

  • 一维数组前缀和

    定义式:

        

 递推式:

    

  • 二维数组前缀和



定义式:

递推式:

    

三、解决什么问题

前缀和、差分是为了解决某一类问题。比如下面这道题目:

【题目描述】

输入一个长度为n的整数序列。接下来再输入M次询问,每个询问输入一对L, R。对于每次询问,输出原序列中从第L个数到第R个数的和。

【输入描述】

第一行包含两个整数n和m。

第二行包含n个整数,表示整数数列。

接下来m行,每行包含两个整数l和r,表示一个询问的区间范围。

【输出描述】

共m行,每行输出一个询问的结果。

【样例输入】

5 3  2 1 3 6 4  1 2  1 3  2 4

【样例输出】

3  6  10

【数据范围】

1≤l≤r≤n,

1≤n,m≤100000,

−1000≤数列中元素的值≤1000

看到这个题目可能很多人会想到暴力求解方法,比如下面这样:

#include<iostream>  using namespace std;  int main(){      int n,m,sum=0;      int a[100];       cin>>n>>m;      for(int i=1;i<=n;i++){          cin>>a[i];      }      //暴力求解      while(m--){          int L,R;          int sum=0;          cin>>L>>R;          for(int i=L;i<=R;i++){              sum+=a[i];          }          cout<<"sum="<<sum<<endl;        }          return 0;  }



按照上面这样做的时间复杂度为O(n*m),如果nm的数据量稍微大一点就很可能超时,如果我们使用前缀和的方法来做的话就能够将时间复杂度降到O(n+m),提高了运算效率,避免超时问题。

 核心代码:

for (int i = 1; i <= n; i ++ ){  	cin >> a[i];  	s[i] = s[i - 1] + a[i];  }

特别是s[i]=s[i-1]+a[i]这行代码,这行代码拆解如下:

s[1]=s[0]+a[1]; //s[0]为0  s[2]=s[1]+a[2]=a[1]+a[2];  s[3]=s[2]+a[3]=a[1]+a[2]+a[3];  s[4]=s[3]+a[4]=a[1]+a[2]+a[3]+a[4];  ......

按照上面这样,我们就能得到前缀和了。

我们这么做的优势在于:以O(1)的时间复杂度得到某块区间的总和。

那么,我们求区间和的过程就相对简单了。

因为L<=R, 所以我们可以参考下面的过程:

s[L]=a[1]+a[2]+a[3]+....+a[L]

s[R]=a[1]+a[2]+a[3]+...+a[L]+a[L+1]+a[L+2]+....+a[R]

我们要求的是a[L]+a[L+1]+...+a[R]。也就是s[R]-s[L-1];









然后我们的代码就可以参考下面这样写:

#include<iostream>  using namespace std;  int main(){      int n,m;      int a[100]={0};  	int s[100]={0};       cin>>n>>m;      for(int i=1;i<=n;i++){          cin>>a[i];          s[i]=s[i-1]+a[i];      }      //前缀和      	 while (m--){          int l, r;          cin >> l >> r;          cout << s[r] - s[l - 1] << endl;      }      return 0;  }



(adsbygoogle = window.adsbygoogle || []).push({});

作者:亿万年的星光 分类:趣味小程序 浏览: