算法模板:前缀和、差分

算法模板,前缀和

前缀和
设存在数组 a1,a2,a3,a4,……an;
前缀和数组Si = ai + a2+ a3 +……+ai;这里一定要注意下标是从1开始;
前缀和的作用
能够快速求出原数组的一段和;

代码

#include

using namespace std;
const int N = 1e6 + 10;

int a[N], s[N];
int m,n;

int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 1 ; i <= n; i ++ )
        scanf("%d",&a[i]);
    for(int i = 1 ; i <= n ; i ++ )
        s[i] = s[i-1] + a[i];
   
    while(m--)
    {
         int l , r;
        scanf("%d%d",&l,&r);
        printf("%d\n",s[r] -s[l-1]);
    }
    return 0;
}

代码分析
s[r] = a1 + a2 + a3 +a4 + a5+…… + al-1 + al + ……+ ar-1 + ar;
s[l-1] = a1+ a2 + a4 + a4 + a5 +……+ al-1;
上下两个式子进行相减就可以得到我们需要的式子了;
为什么下标从1开始:
在任何情况下保证s[r] -s[l-1] = al+……+ar;

二维前缀和

0、二维的前缀和表示ij这个点的左上方的矩阵的和

#include

using namespace std;

const  int N = 1010 ;
int n, m, q;
int a[N][N],s[N][N];

int main(void)
{
    scanf("%d%d%d",&n,&m,&q);
    for(int i = 1; i <= n; i ++ )
        for(int j = 1; j <= m; j ++ )
            scanf("%d",&a[i][j]);
    for(int i = 1; i <= n; i ++)
        for(int j =1 ; j <= m; j ++ )
            s[i][j] += s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j];
    while(q--)
    {
        int x1,x2,y1,y2;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        printf("%d\n",s[x2][y2] - s[x1-1][y2]-s[x2][y1-1] + s[x1-1][y1-1]);  
    }
    return 0;
}

代码分析
s[i][j]表示ij点左上方矩形矩阵的和,计算的时候要注意减去重复的部分;

差分

0、前缀和 和差分是一对逆运算;
1.假设存在一个数组如 a1,a2,a3,a4,a5,……,an;构造一个数组b1,b2,b3,…bn;使得ai为bi的前缀和(A中的每一个元素是b的前缀和)则对b求一遍前缀和就可以得到a数组
2、差分的作用:由于a是b的前缀和,那么我们在B数组中的任意一项上添加一个常数c,那么对于A数组对应位置的那一项,对其以及其后面的每一个元素都加上了常数c ,这一点由前缀和的定义不难得到;
3、如何构造差分数组B:
在这里我们可一个据定义来求:
b1 = a1;
b2 = a2 -a1;
b3 = a3 -a2;

bn = an -an-1;
我们还有第二种方法:
插入法:我们根据0-2中所述的性质不难得出,如果我们想在一段A数组上进行常数的添加运算,我们可以利用在数组的最左边元素 l +=c,最右边元素的右边r+1位置进行-=c运算,如代码所示

#include

using namespace std;

const int N = 1e6 + 10;
int n,m;

int a[N],b[N];
void insert(int l , int r, int c)
{
    b[l] += c;
    b[r+1] -= c;
}

int main(void)
{
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i ++ )
    {
        scanf("%d",&a[i]);
    }
    for(int i = 1; i <=n; i ++ )
        insert(i,i,a[i]);
    while(m--)
    {
        int l,r,c;
        cin>>l>>r>>c;
        insert(l,r,c);
    }
    for(int i = 1; i <= n ; i ++ )
    {
        b[i] += b[i-1];
        printf("%d ",b[i]);
    } 
    return 0;
}

二阶矩阵的差分

0、设存在A这个矩阵,则我们构造B矩阵,使得A中的ij是B中对应位置的前缀和;

1、二位矩阵的差分的构造方法:
在这里我们采用的还是利用那个插入的方法,先假设AB数组均初始化为0,然后依次插入元素;

#include

using namespace std;

const int N = 1010;

int  n,m,q;
int a[N][N],b[N][N];

void insert(int x1, int y1, int x2, int y2, int c)
{
    b[x1][y1] += c; 
    b[x2+1][y1] -=c;
    b[x1][y2+1] -= c;
    b[x2+1][y2+1] +=c;
}

int main(void)
{
    cin>>n>>m>>q;
    for(int i = 1; i <= n; i ++ )
        for(int j = 1; j <= m; j ++ )
            scanf("%d",&a[i][j]);
    for(int i = 1; i <= n; i ++ )
        for(int j = 1; j <= m; j ++ )
            insert(i,j,i,j,a[i][j]);
    while(q--)
    {
        int x1,x2,y1,y2,c;
        cin>>x1>>y1>>x2>>y2>>c;
        insert(x1,y1,x2,y2,c);
    }
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= m; j ++ )
            b[i][j] += b[i-1][j ] + b[i][j-1] -b[i-1][j-1];
    for(int i =1; i<=n;i++ )
    {
        for(int j = 1; j <= m; j ++ )
            printf("%d ",b[i][j]);
        puts("");
            
    }
    return 0;
}

代码分析

 b[x1][y1] += c; 
    b[x2+1][y1] -=c;
    b[x1][y2+1] -= c;
    b[x2+1][y2+1] +=c;

在这里是那个插入函数,我们要在矩阵的一个子矩阵使得对用元素均加上一个常数;可以借助画图帮助理解;

你可能感兴趣的:(算法,数据结构,c++)