算法笔记(九)前缀和与差分

前缀和:
前缀和可以理解为某个序列前n项的和,不限于一维还是二维。对于一个数列an,前i项的前缀和Si = a1 + a2 + a3 + … + ai

差分:
差分可以理解为前缀和的逆运算,对于一个序列an,对于任意相邻的两项ai和a(i+1),二者之差即|ai-a(i+1)|即为此数列在i处的差分。

前缀和的应用。
例一:一维前缀和
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
常规思路:对与任意的l与r,遍历al到ar进行求和操作,然后输出结果。时间复杂度:O(m*(r-l))最坏情况O(n²)
前缀和思路:对序列进行前缀和求和,对于任意的l到r之间的和,都可表示为sr - s(i-1)
在这里插入图片描述
利用前缀和的思想,可以将O(n²)的复杂度的算法优化为O(n+m)的算法。
代码如下:

#include<iostream>
using namespace std;
const int MAXN=1e5+7;
int a[MAXN];
int s[MAXN];
int main()
{
    int n,m;
    cin>>n>>m;
    s[0]=0;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];//输入序列的值
        s[i]=s[i-1]+a[i];//进行前缀和求和
    }
    for(int i=1;i<=m;i++)//进行查询操作
    {
        int x,y;
        cin>>x>>y;
        cout<<s[y]-s[x-1]<<endl;//前缀和作差求区间和
    }
    return 0;
}

例二:二维前缀和
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
朴素算法思路:对于一个二维矩阵,要知道矩阵上面每一个RxR正方形内的权值之和,进行for循环枚举,遍历每一个满足条件的正方形的解并计算权值和,时间复杂度:O(n²RR)
前缀和思路:可以利用二维前缀和来求解,对于坐标aij,前缀和Sij表示从a11到aij围成的矩形的权和。
在这里插入图片描述
假设蓝色区域为最优区域为我们所求,由图所示以及包含排斥定理和容斥定理可知,S = Sij - Si(j-R) - S(i-R)j + S(i-R)(j-R)

#include<iostream>
#include<cstring>
using namespace std;
const int MAXN=5005;
int r,n;
int f[MAXN][MAXN];
int w[MAXN];
int main()
{
    memset(f,0,sizeof(f));
    cin>>n>>r;
    for(int i=1;i<=n;i++)
    {
        int x,y,z;
        cin>>x>>y>>z;
        f[x+1][y+1]+=z;
    }
    for(int i=1;i<=5001;i++)
    {
        for(int j=1;j<=5001;j++)
        {
            f[i][j]+=f[i-1][j]+f[i][j-1]-f[i-1][j-1];
        }
    }
    int maxn=0; 
    for(int i=r;i<=5001;i++)//必须都从r开始,否则会出现a[负数]
    {
        for(int j=r;j<=5001;j++)
        {
            if(maxn<f[i][j]-f[i-r][j]-f[i][j-r]+f[i-r][j-r])
                maxn=f[i][j]-f[i-r][j]-f[i][j-r]+f[i-r][j-r];
        }
    }
    cout<<maxn<<endl;
    return 0;
}

差分:对于一个序列an,它的差分bn=an-a(n-1)
在这里插入图片描述
差分的应用:
在这里插入图片描述
例三:一维差分
在这里插入图片描述在这里插入图片描述在这里插入图片描述
思路说明:
要想让每一个数都相等,则需要满足差分bn全部为0。即b2、b3、、bn均为0
按照差分的区间操作原理(如上图),对区间进行+1或者-1,差分序列一增一减。故按照贪心思想,让正减小负增加,一正一负配对。全部匹配完之后剩余的可以跟b1或者bn+1进行增减匹配,因为b1对整个差分序列没有影响。
因此最少操作次数为:min(正数的和,负数的和)+abs(正数的和-负数的和)
最终的到的结果数为:abs(正数的和-负数的和)+1
解释最终得到的结果数(上一行)。当剩余的数都对b1进行加减差分时,b1变化abs(正数的和-负数的和),当剩余的数都对bn+1进行加减差分时,b1变化为0,故b1一共有abs(正数的和-负数的和)+1种答案
代码如下:

#include<iostream>
#include<cmath>
using namespace std;
const int MAXN=1e5+7;
int a[MAXN];
int b[MAXN];
int main()
{
    int n;
    cin>>n;
    a[0]=0;
    long long int zheng=0,fu=0;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        b[i]=a[i]-a[i-1];
    }
    for(int i=2;i<=n;i++)
        if(b[i]>0)
            zheng+=b[i];
        else 
            fu-=b[i];
    cout<<min(zheng,fu)+abs(zheng-fu)<<endl;
    cout<<abs(zheng-fu)+1<<endl;
}

例四:一维差分
在这里插入图片描述在这里插入图片描述在这里插入图片描述
思路:每两头牛可以互相看到对方,则说明两头牛之间的牛都比这两头牛矮,而已知了只有一头牛的身高,所以可以起初假设所有牛一样高,然后两头牛都能看到对方,则将两头牛之间的牛的身高-1,算出相对的身高差分,最后差分求和即可。
代码如下:

#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
const int MAXN = 1e4+7;
int c[MAXN],d[MAXN];
bool point[MAXN][MAXN];
int main()
{
    int n,p,h,m;
    memset(c,0,sizeof(c));
    cin>>n>>p>>h>>m;
    for(int i=1;i<=m;i++)
    {
        int x,y;
        cin>>x>>y;
        if(x>y)
            swap(x,y);
        if(point[x][y]==false)
            c[x+1]--,c[y]++,point[x][y]=true;
    }
    for(int i=1;i<=n;i++)
        d[i]=d[i-1]+c[i];
    for(int i=1;i<=n;i++)
        cout<<h+d[i]<<endl;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值