算法学习笔记(3)-差分

#差分

差分和前缀和互为逆运算:

给定一个原数组s,差分数组h,两者的关系如下所示:

s[i] = h[1] + h[2] + h[3] + …… +h[i]

针对于上面的公式,由差分数组h推导而来

h[1] = s[1]

h[2] = s[2] - s[1]

h[3] = s[3] - [2]

……

h[n] = s[n] - s[n-1]

将上述公式加起来接得到了:s[i] = h[1] + h[2] + h[3] + …… +h[i]

因此原数组s是差分数组h的前缀和,差分数组h为原数组s的差分。

 差分的用途:

 构造一个数组的差分矩阵(差分数组),是为了针对频繁的对数组中某个区间进行同一操作。

示例:

将序列中[l,r]之间的每一个数加上c这一操作,可以执行n次,每次的c都不相同,如果直接对原数组进行操作,每次操作都会花费O(n)的时间复杂度,总体的时间复杂度就变成了O(n^2)。但是如果使用该数组的差分矩阵(差分数组)进行操作,会将每一次的操作的时间复杂度降为O(1),整体的时间复杂度就变成了O(n),说白了利用了一种特殊的数学性质,利用空间来换时间。然后根据前缀和和差分就能得到具体的答案。

#一维差分

所谓的一维差分就是在我们已有一维数组的基础上构造一个差分数组,此时差分数组需要初始化操作,时间复杂度O(n);

具体的代码示例:

//c++基础差分示例代码
h[0] = s[0]
for (int i = 1 ; i < n ; i++){h[i] = s[i] - s[i-1] ;}//c++可以边获取元素边进行差分操作
int n,m,s[100005],h[100005] ;
//差分操作-对一个区间进行操作
void Add(int c , int l , int r)
{h[l] += c ;h[r+1] -= c ;
}
int main()
{
scanf("%d %d",&n,&m) ;
//初始化差分数组for (int i = 1 ; i <= n ; i++)
{scanf("%d",&s[i]) ;Add(s[i],i,i) ;
}
//详细说明一下这个初始化操作的过程-我们初始化是从1 - n,闭区间进行的操作
// s[1] , h[1] += s[1] , h[2] -= s[1] ,                 h[1] = s[1]
// s[2] , h[2] += s[2] , h[3] -= s[2] ,第一个差分出来了, h[2] = s[2] - s[1]
// s[3] , h[3] += s[3] , h[4} -= s[3] ,第二个差分出来了, h[3] = s[3] - s[2]
//以此类推                                              h[n] = s[n] - s[n-1]
}
//python代码示例-1
s = [0] * 100005
h = [0] * 100005//差分操作-对一个区间进行操作
def add(c , l , r)h[l] += c h[r+1] -= c n,m = map(int,input().split())for i in range(1,n):s[i] = int(input())add(s[i],i,i)//python代码示例-2
s = [map(int,input().split())]
h = [0] * len(s)
h[0] = s[0]
for i in range(1,n) :h[i] = s[i] - s[i - 1] 

 恢复操作完成的数组-采用前缀和进行恢复-二者互为逆运算:

具体的代码示例:

//c++代码示例-利用前缀和来进行恢复,二者互为逆运算
for (int i = 1 ; i < n ; i++)
{h[i] += h[i-1] ;printf("%d",h[i]) ;
}
#python代码示例
for i in range(1,n) :h[i] += h[i-1]print(h[i])

 ##实战演练

//c++代码示例
#include <iostream>
using namespace std ;const int N = 100001 ;
int n,m ;
int s[N],h[N] ;void add(int c,int l,int r)
{h[l] += c ;h[r+1] -= c ;
}
//示例1
int main()
{scanf("%d%d",n,m) ;for (int i = 1 ; i <= n ; i++){scanf("%d",&s[i]) ;}for (int i = 1 ; i<= n ; i++){add(s[i],i,i) ;}while (m--){int l,r,c ;scanf("%d%d%",&l,&r,&c) ;add(c,l,r) ;}for (int i = 1 ; i<= n ; i++){h[i] += h[i-1] ;printf("%d ",h[i]) ;}return 0 ;
}
//示例2
int main()
{cin >> n >> m ;for (int i = 1 ; i<=n ; i++){cin >> s[i] ;h[i] = h[i] - h[i-1] ;}while (m--){int l,r,c ;cin >> l >> r >> c ;h[l] += c ;h[r+1} -= c ;}for (int i = 1 ; i <= n ; i++){h[i] = h[i] + h[i - 1] ;cout << h[i] << " " ;}return 0 ;
}
//python代码示例n,m = map(int,input().split())s = [map(int,input().split())]
h = [0] * (len(s))
h[0] = s[0]for i in range(1,n):h[i] = h[i] - h[i-1]for _ in range(m):l,r,c = map(int,input().split())h[l] += ch[r+1] -= cfor i in range(1,n):h[i] = h[i] + h[i - 1]print(h[i],end = " ")

#二维差分

所谓的二维差分就类似在二维数组进行一个数值更新的过程,你也可以理解成为由上一步的结果推导出来下一步的结果。

具体的操作是选取了一块区域进行操作,因此在考虑区域更改,就要考虑方向性问题:

1.差分数组的更新过程

//c++示例代码
void insert(int x1, int y1, int x2,int y2)
{h[x1][y1] += c ;h[x2+1][y1] -= c ;h[x1][y2+1] -= c ;h[x2+1][y2+1] += c ;
}
#python代码示例
def void(x1,y1,x2,y2) :h[x1][y1] += c h[x2+1][y1] -= c h[x1][y2+1] -= c h[x2+1][y2+1] += c 

2.差分数组初始化

//c++代码
for (int i = 1 ; i <= n ; i++)
{for (int j = 1 ; j <= n ; j++){insert(i,j,i,j,s[i][j]) ;}
}
#python代码示例
for i in range(1,n+1):for j in range(1,n+1):insert(i,j,i,j,s[i][j)

3.对结果的恢复-逆运算求前缀和

//c++代码示例
for (int i = 1 ; i <= n ; i++)
{for (int j = 1 ; j <= n ; j++){h[i][j] += h[i-1][j] + h[i][j-1] + h[i-1][j-1] ;print("%d",h[i][j]) ;}puts("") ;
}
//python代码示例
for i in range(1,n+1):for j in range(1,n+1):h[i][j] += h[i-1][j] + h[i][j-1] + h[i-1][j-1]print(h[i][j],end=" ")print()

 ##实战演练

##代码示例

//c++代码示例
#include <iostream>
using namespace std ;const int N = 1010 ;
int n,m,q ;
int s[N][N],h[N][N] ;void insert(int x1, int y1, int x2, int y2, int c)
{h[x1][y1] += c ;h[x2+1][y1] -= c ;h[x1][y2+1] -= c ;h[x2+1][y2+1] += c ;
} int main()
{scanf("%d%d%",&n,&m,&q) ;for (int i = 1 ; i <= n ; i++){for (int j = 1 ; j <= m ; j++){scanf("%d",&s[i][j]) ;}}for(int i = 1; i <= n; i++){for(int j = 1; j <= m; j++){insert(i,j,i,j,s[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++){h[i][j] += h[i-1][j] + h[i][j-1] - h[i-1][j-1];}}for(int i = 1; i <= n; i++){for(int j = 1; j <= m; j++){printf("%d ",h[i][j]);}puts("");}   return 0;}

2

#python代码示例def insert(x1, y1, x2, y2, c):h[x1][y1] += ch[x2 + 1][y1] -= ch[x1][y2 + 1] -= ch[x2 + 1][y2 + 1] += cn, m, q = map(int, input().split())
h = [[0] * (m+2) for _ in range(n+2)]
mp = [[0] * (m+2)]
# print(mp)
for _ in range(n):mp.append([0] + list(map(int,input().split())) + [0])
# print(mp)for i in range(1, n+1):for j in range(1, m+1):insert(i, j, i, j, mp[i][j])for _ in range(q):x1,y1,x2,y2,c = map(int , input().split())insert(x1, y1, x2, y2, c)for i in range(1, n+1):for j in range(1, m+1):h[i][j] += h[i - 1][j] + h[i][j - 1] - h[i - 1][j - 1]
# print(h)
for i in range(1,n+1):for j in range(1,m+1):print(h[i][j], end=" ")print()

#总结

树状数组插入和查询都可以优化到O(logn)。差分和前缀和适合用在查询或修改次数十分巨大的时候,当修改和查询在同一复杂度时适合用树状数组。

前缀和与差分可以理解为互逆的 ,求前缀和 跟 差分通常设数组首项下标为1,方便思考的计算。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/695665.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

什么是 RAG,大模型微调,向量数据库的应用场景

原来向量数据库的应用场景是这样的&#xff01;按照我的理解&#xff0c;大模型其实是没有学习能力的&#xff0c;它就相当于一个真值表或者矩阵&#xff0c;给它输入&#xff0c;它就输出&#xff0c;在使用它的过程中它不会自己训练自己&#xff0c;改变既有的参数&#xff0…

【深度学习】实验3 特征处理

特征处理 python 版本 3.7 scikit-learn 版本 1.0.2 1.标准化 from sklearn.preprocessing import StandardScaler from sklearn.preprocessing import MinMaxScaler from matplotlib import gridspec import numpy as np import matplotlib.pyplot as plt cps np.random.…

开源免费的定时任务管理系统:Gocron

Gocron&#xff1a;精准调度未来&#xff0c;你的全能定时任务管理工具&#xff01;- 精选真开源&#xff0c;释放新价值。 概览 Gocron是github上一个开源免费的定时任务管理系统。它使用Go语言开发&#xff0c;是一个轻量级定时任务集中调度和管理系统&#xff0c;用于替代L…

免费获取SSL证书的几种方法

免费获取SSL证书的方法有很多种&#xff0c;以下是一些常见的途径&#xff1a; 1、Lets Encrypt&#xff1a;Lets Encrypt是一个由非营利组织提供的免费SSL证书服务&#xff0c;其安装部署简单、方便&#xff0c;且已被Firefox、Chrome、IE等浏览器所支持。您可以通过其官方网…

C++入门——引用(2)

前言 上一节我们开始学习了C&#xff0c;并且对C有了初步的了解&#xff0c;这一节我们继续学习C的基础&#xff0c;那么废话不多说&#xff0c;我们正式进入今天的学习 C中的引用 1.1引用的概念 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0…

【Ubuntu永久授权串口设备读取权限“/dev/ttyUSB0”】

Ubuntu永久授权串口设备读取权限 1 问题描述2 解决方案2.1 查看ttyUSB0权限&#xff0c;拥有者是root&#xff0c;所属用户组为dialout2.2 查看dialout用户组成员&#xff0c;如图所示&#xff0c;普通用户y不在dialout组中2.3 将普通用户y加入dialout组中2.4 再次查看dialout用…

pycharm虚拟环境

File->setting->project->Python interpreter 路径必须写全

视频剪辑批量转码技巧:如何将MP4视频快速转换为MP3音频的方法

在视频剪辑和音频处理的领域中&#xff0c;经常需要将视频文件转换为音频文件&#xff0c;特别是将MP4视频转换为MP3音频。这样的转换不仅可以减少文件大小&#xff0c;方便传输和存储&#xff0c;还可以在不损失音频质量的情况下&#xff0c;方便在各种设备上播放。下面&#…

从XML配置角度理解Spring AOP

1. Spring AOP与动态代理 1.1 Spring AOP和动态代理的关系 Spring AOP使用动态代理作为其主要机制来实现面向切面的编程。这种机制允许Spring在运行时动态地创建代理对象&#xff0c;这些代理对象包装了目标对象&#xff08;即业务组件&#xff09;&#xff0c;以便在调用目标对…

使用gRPC基于Protobuf传输大文件或数据流

文章目录 使用gRPC基于Protobuf传输大文件或数据流1. 背景和技术选择1.1 gRPC的优势1.2 Protocol Buffers的优势 2. 项目配置与环境搭建2.1 安装gRPC和Protocol Buffers2.1.1 安装Cmake2.1.2 设置环境变量2.1.3 安装必要的依赖2.1.4 下载gRPC源码2.1.5 编译gRPC和 [Protocol Bu…

2024年淘宝天猫618超级红包领取口令活动时间是从什么时候开始到几月几号结束?

2024年淘宝天猫618活动&#xff0c;将于2024年5月19日开始&#xff0c;今年618淘宝天猫取消了预售环节。同时&#xff0c;618淘宝天猫也提供了多项优惠活动&#xff1a;超级红包、跨店满减、官方立减、全程价保及草柴APP领优惠券拿购物返利等多重优惠活动。 2024年淘宝天猫618…

2024年抖店什么类目赚钱?这八个类目最赚钱,想开店的快来瞅瞅!

哈喽~我是电商月月 做抖音小店的商家都知道&#xff0c;选品是非常重要的 那什么样的商品类型赚钱&#xff0c;哪些商品又适合新手操作呢? 今天我就给大家推荐几个热销类目&#xff0c;特别是最后两个&#xff0c;下半年说不定会小爆一把哦 一&#xff0e;日用百货 这个类…