栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

前缀和(一维前缀和,二维前缀和),差分(差分对定义,作用)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

前缀和(一维前缀和,二维前缀和),差分(差分对定义,作用)

目录

前缀和

一维前缀和

查询一个字符串区间[L,R]中有多少个字符d,d为‘a' 或 ’z'

二维前缀和

给定一个二维矩阵,每次给定四个点代表矩阵范围,如何O(1)的求该矩阵内所有数的和(简称矩阵和)

差分

那么考虑一下,对于什么数组求前缀和可以得到原数组呢

差分的定义

作用

Q次操作,每次让下标在区间[L,R]的所有数加上x, 最后让你输出原数组的每个数

力扣5459给你一个目标序列S,让你从全0序列开始做区间+1,问你最少多少次能够造出目标序列

洛谷P4552 [Poetize6] IncDec Sequence


前缀和

一维前缀和

一维前缀和的思想是动态规划,常见的转移为 S[i] = S[i-1] + a[i],i位置的前缀和等于i-1位置的前缀和加上序列中的a[i]

查询一个字符串区间[L,R]中有多少个字符d,d为‘a' 或 ’z'

对于这个题目,我们可以得出转移式 S[i] = S[i-1] + a[i] == 'a' || a[i] == 'z',这样一来我们就可以对区间进行O(1)的求出答案

二维前缀和

给定一个二维矩阵,每次给定四个点代表矩阵范围,如何O(1)的求该矩阵内所有数的和(简称矩阵和)

对于这个题目,我们首先思考前缀和的转移,S[i][j]为从从矩阵开头到i,位置内的所有数的和,那么转移式为S[i][j] = S[i-1][j] + S[i][j-1] -S[i-1][j-1] + a[i][j](左部分和+上部分和减去重合部分加上矩阵当前位置的元素),这样要求四点中矩阵内的和,只需要用右下角的二维前缀和减去左上角的二维前缀和即可

差分

首先对于一个数组,我们做一遍前缀和操作,就可以得到前缀和数组

那么考虑一下,对于什么数组求前缀和可以得到原数组呢

对于这个问题,我们可以对原数组做一个差分操作,那么转移式为

那么我们可以得到b = {1, 1, 2, -1},我们只需要对此数组做前缀和操作即可得到原数组

差分的定义

 差分是一种与前缀和相对的一种行为,它们互为逆运算,对原数组进行前缀和得到前缀和数组,对原数组进行差分得到差分数组 

作用 O(1)的处理 区间加法

Q次操作,每次让下标在区间[L,R]的所有数加上x, 最后让你输出原数组的每个数

解题思路:

最开始读入原数组,把他变成差分数组,然后对差分数组进行操作,然后最后做一遍前缀和还原 成原数组即可。

思考:为什么对差分数组能做到的O(1)区间加法?

首先我们考虑对差分数组的第i个位置+x,因为我们最终要做前缀和还原数组,所以这个位置+x会影响后面所有的元素,这个x会被前缀和操作往后传递

这样的行为我们称为标记传递,有了这样的思路之后我们在考虑如何让[L,R]的数+x,而后面的地方不受影响呢

所以我们有Q次操作,每次对差分数组打标记,b[l] += x,b[r+1] -= x,最后做前缀和即可实现题目要求,即如下图所示,消除前一个x的影响 

#include
using namespace std;

#define ll long long
const ll maxn = 1e9 + 5;

int main()
{
    //Q次操作
    int q;
    //原数组,差分数组,前缀和数组
    int a[11] = {0}, b[11] = {0}, s[11] = {0};
    cin >> q;
    //先求出原数组的差分数组
    for(int i = 1; i <= 10; i++)
    {
        if(i == 1)b[i] = a[i];
        else b[i] = a[i] - a[i-1];
    }
    while(q--)
    {
        int l, r, x;
        cin >> l >> r >> x;
        //对差分数组做传递标记
        b[l] += x;
        b[r+1] -= x;
    }
    //最终求出前缀和数组
    for(int i = 1; i <= 10; i++)
    {
        s[i] = s[i-1] + b[i];
        cout << s[i] << " ";
    }
    return 0;
}

力扣5459给你一个目标序列S,让你从全0序列开始做区间+1,问你最少多少次能够造出目标序列

题目链接:无

给你一个目标序列S,让你从全0序列开始做区间+1,问你最少多少次能够造出目标序列

解题思路:

假如目标序列S = {3, 6, 4, 2, 7},那么我们得到差分数组b = {3, 3, -2, -2, 5},由于题目给出了每次只能区间+1,这对应着,我们至少在i的位置需要有一个b[i]次的操作,所以我们只要统计b数组内的正整数即可

#include
using namespace std;

#define ll long long
const ll maxn = 1e9 + 5;

int main()
{
    int a[105] = {0}, b[105] = {0};
    int n, ans = 0;
    cin >> n;
    for(int i = 1; i <= n; i++)cin >> a[i];
    //对数组进行差分
    for(int i = 1; i <= n; i++)
    {
        if(i == 1)b[i] = a[i];
        else b[i] = a[i] - a[i-1];
    }
    //求差分数组中的正数和
    for(int i = 1; i <= n; i++)
    {
        if(b[i] > 0)ans += b[i];
    }
    cout << ans;
    return 0;
}

洛谷P4552 [Poetize6] IncDec Sequence

题目链接:洛谷P4552 [Poetize6] IncDec Sequence

解题思路:

对于给出的数组我们进行差分操作,得到一个序列,根据差分的性质可以得出,每一个当前位置的元素会对后产生传递影响,所以题目要求将所有的数变为一样,我们只需要将b[2]到b[n]的位置全部变为0即可,我们对差分数组的[L,R]进行操作时,是对L做一个操作,R+1进行反操作,这样会影响还原原数组,然而对于差分数组本身是没有影响的,这样的操作只会改变L和R+1,所以计算出差分数组2-N的正数和与负数和,题目给出的操作为[L,R]可以+1或-1,所以每一次都是+1对应着-1,第一问,那么我们的最小操作次数就是正数与负数绝对值的最大数,第二问,因为我们最大数会比最小数多出来几次操作,我们可以将这个操作算到b[1]上,又因为b[1]不操作也算一次,所以第二问多少种可能就是正数与负数绝对值的差值+1

#include
using namespace std;

#define ll long long
const ll maxn = 1e5 + 5;

int main()
{
    ll n, up = 0, down = 0;
    ll a[maxn];
    cin >> n;
    for(int i = 1; i <= n; i++)cin >> a[i];
    for(int i = 2; i <= n; i++)
    {
        //对差分数组的第二项到最后一项进行正数和负数的分别相加
        if(a[i] - a[i-1] > 0)up += a[i] - a[i-1];
        else down -= a[i] - a[i-1];
    }
    //最少操作次数是二者的最大值,最多可能是二者的差
    cout << max(up, down) << endl << max(up, down)-min(up, down)+1;
    return 0;
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/928189.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号