鱼C论坛

 找回密码
 立即注册
查看: 517|回复: 10

[技术交流] 差分算法(仅总代码收费)

[复制链接]
发表于 2023-7-23 10:32:39 | 显示全部楼层 |阅读模式
差分是重要的区间专用数据结构,它有与前缀和不同的优势……
差分数据结构与前缀和正好相反,我们举个例子:
数组012345
a[]025171 9
b[]027242534

假如a[]是输入的数据,那么b[]是a[]的前缀和,这与我们上节课的定义基本一样,那如果b[]是输入数据,那么a[]是b[]的差分。大家可以理解上面那句话的意思了吗?如果不理解,我们通过一个例子:
数组0123455
a[]025171 90
b[]0


我们如何构建a[]的差分呢?那么其实很简单,就是当前位置的数字减掉前面位置的数字。比如b[1]=a[1]-a[0],以此类推。构建出来的数组如下:
数组0123455
a[]025171 90
b[]02312-168-9

这就创建好了一个差分数组。构建差分数组的代码如下:
  1. #include <iostream>
  2. #include <stdlib.h>
  3. using namespace std;
  4. int a[1001]={0}, b[1001];
  5. int n;

  6. int main(void){
  7.         cin>>n;
  8.         for (int i=1; i<=n; i++){
  9.                 cin>>a[i];
  10.         }
  11.         for (int i=1; i<=n+1; i++){
  12.                 b[i]=a[i]-a[i-1];
  13.         }
  14.         return 0;
  15. }
复制代码

然后就是我们上节课的三件套:区间添加数,区间求和,区间最大值。而差分最优秀的地方就在于区间添加数。其原理也十分巧妙。

假如我们以上面例子,在[2,4]的中间区间添加数,那么做法很简单,在2的位置+4,在5的位置-4,此时有的同学肯定不理解为什么。

我们画一个图出来:
[attach]174013[/attach]
我们需要知道,在2位置+4是对后面的所有数都+4的,因为差分仅保存两数之间距离,当它根据两数间距离恢复成前缀和数组时,就会知道它对每一个数都+4。那在5位置-4也是同理的。那么在5之后的位置既+4,又-4,就相当于抵消掉了,就只剩下2~4的区间添加了4,我们所需要的任务也完成了,正如下图一样:
[attach]174014[/attach]
代码的写法很简单:
  1. int add(int x, int y, int p){
  2.     b[x]+=p;
  3.     b[y+1]-=p;
  4. }
复制代码

然后我们来讲讲区间求和。因为差分数组比较特殊,记的是两个数之间的大小关系,所以我们必须转化为简单的方式,转化为普通数组的形式,然后使用普通数组做简单的加法的形式去输出就可以了。比如说以下例子:
数组0123456
b[]02712-164-9

我们就应该根据b数组的差分形式重新构建普通数组,构建方法很简单,就是通过从1开始循环到6,然后将前面一位数的值加上当前差分的值,存入c[]即可,比如在c[1]的位置,就加入b[1]的差分结果,在c[2]的位置,就是c[1]+b[2]的结果。遍历之后的结果如下:
数组0123456
b[]02712-164-9
b[]02921590
然后区间求和的话就循环求和,没有什么难的,代码如下:
  1. int sum(int x, int y){
  2.     int c[1000]={0};
  3.     for (int i=1; i<=n+1; i++){
  4.         c[i]=c[i-1]+b[i];
  5.     }
  6.     int ans=0; //存储总和的变量
  7.     for (int i=x; i<=y; i++){
  8.         ans+=c[i];
  9.     }
  10.     return ans;
  11. }
复制代码

当然,该程序还可以优化,方法就是将c[]改为c动态变量,那么每一个循环就得到当前的数字,那么我们只要将属于区间内的数字相加即可,不理解的自行理解程序……代码如下:
  1. int sum(int x, int y){
  2.     int c=0, ans=0;
  3.     for (int i=1; i<=n+1; i++){
  4.         c+=b[i];
  5.         if (i>=x&&i<=y){
  6.             ans+=c;
  7.         }
  8.     }
  9.     return ans;
  10. }
复制代码

总的来说,程序的时间复杂度为O(n)
最后说说区间最大值,方法与区间求和类似,同样先求出原始数组,然后再找区间中的最大值,而优化方法也是用区间变量的方法,代码如下:
  1. int max1(int x, int y){
  2.     int c=0, lmax=0;
  3.     for (int i=1; i<=n+1; i++){
  4.         c+=b[i];
  5.         if (i>=x&&i<=y){
  6.             if (lmax<c){
  7.                 lmax=c;
  8.             }
  9.         }
  10.     }
  11.     return lmax;
  12. }
复制代码

时间复杂度依旧为O(n),速度依旧不快。
购买主题 已有 2 人购买  本主题需向作者支付 2 鱼币 才能浏览
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-7-23 10:33:41 | 显示全部楼层
沙发~学习了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-23 11:07:47 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-7-23 11:37:44 | 显示全部楼层
若 s 是 a 的前缀和数组,那么 a 是 s 的差分
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-23 11:43:59 | 显示全部楼层
zhangjinxuan 发表于 2023-7-23 11:37
若 s 是 a 的前缀和数组,那么 a 是 s 的差分

看群里消息
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-23 11:45:35 | 显示全部楼层

什么群,没有啊,rating马上算
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-23 11:46:56 | 显示全部楼层
zhangjinxuan 发表于 2023-7-23 11:45
什么群,没有啊,rating马上算

就是Evan的atcider用户名错误

输入法不管了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-23 11:52:58 | 显示全部楼层
sfqxx 发表于 2023-7-23 11:46
就是Evan的atcider用户名错误

输入法不管了

???
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-24 15:04:01 | 显示全部楼层
sfqxx 发表于 2023-7-23 11:46
就是Evan的atcider用户名错误

输入法不管了

《Evan》
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-24 15:11:04 From FishC Mobile | 显示全部楼层
Ewan-Ahiouy 发表于 2023-7-24 15:04
《Evan》

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-24 15:26:00 | 显示全部楼层
学习
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-28 16:00

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表