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

news2024/12/26 22:59:36

#差分

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

给定一个原数组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] += c
    h[r+1] -= c

for 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] += c
    h[x2 + 1][y1] -= c
    h[x1][y2 + 1] -= c
    h[x2 + 1][y2 + 1] += c


n, 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.coloradmin.cn/o/1670080.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue前端时间段选择控件

实现效果: 可选具体的某天的某时某分某秒 vue前端代码: <el-form-item label"日期"><el-date-pickerv-model"daterangerq"style"width: 240px"value-format"yyyy-MM-dd HH:mm:ss"type"datetimerange"range-separat…

Spring,SpringMVC,SpringBoot知识总结

1.简述Spring,SpringMVC&#xff0c;SpringBoot各自特点及联系 Spring、Spring MVC 和 Spring Boot 是 Java 开发中常用的三个框架&#xff0c;它们之间有以下关系&#xff1a; Spring&#xff1a;是一个全功能的 JavaEE 应用程序框架。它提供了一系列的解决方案&#xff0c…

【管理咨询宝藏99】离散制造智能工厂战略规划方案

本报告首发于公号“管理咨询宝藏”&#xff0c;如需阅读完整版报告内容&#xff0c;请查阅公号“管理咨询宝藏”。 【管理咨询宝藏99】离散制造智能工厂战略规划方案 【格式】PDF版本 【关键词】智能制造、先进制造业转型、数字化转型 【核心观点】 - 推进EHS、品质一致性、生…

通配符证书260元

通配符SSL证书是一种特殊的数字证书&#xff0c;可以保护域名以及该域名下的所有子域名。不论是个人开发者还是企业开发者&#xff0c;只需要购买一个通配符SSL证书&#xff0c;就可以为多个子域名提供传输数据加密服务&#xff0c;降低了多子域名网站开发者购买SSL证书的成本。…

ArcGIS水文水环境数据编辑、管理、处理与分析;ArcGIS水文分析及流域特征提取;湖泊水库水环境监测及评价;河道水污染预测与水环境容量计算等案例实践

目录 专题一 ArcGIS&#xff1a;数据管理 专题二 ArcGIS&#xff1a;数据转换 专题三 ArcGIS&#xff1a;地图制作 专题四 水文水环境数据编辑与管理 专题五 水文水环境数据处理与分析 专题六 ArcGIS水文分析及流域特征提取 专题七 湖泊水库水环境监测及评价 专题八 河…

redis服务连接及常规操作和redis服务getshell

简介 Redis&#xff0c;英文全称是Remote Dictionary Server&#xff08;远程字典服务&#xff09;&#xff0c;是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API。 redis服务于传统服务器不同&…

flink尚硅谷

flink 1 flink基础使用1.1 角色1.2 部署模式&#xff08;抽象&#xff09;1.2.1 会话模式1.2.2 单作业模式1.2.3 应用模式 1.3 运行模式&#xff08;实际 谁来管理资源&#xff09;1.3.1 Stand alone1.3.2 YARN运行模式&#xff08;重点&#xff09; 2. 运行时架构2.1 系统架构…

程序员如何避免35岁危机?

所谓的“35岁危机”通常是指在职业生涯中遇到的一个挑战阶段&#xff0c;这个概念在不同行业和个人中有不同的体现。对于程序员来说&#xff0c;这可能与技术更新迅速、工作强度大、职业发展路径的不确定性等因素有关。 以下是一些建议&#xff0c;帮助程序员避免或缓解这一危机…

三丰云免费虚拟主机与免费云服务器评测

三丰云是一家知名的云计算服务提供商&#xff0c;提供免费虚拟主机和免费云服务器的服务。今天我们就来为大家介绍一下三丰云的免费虚拟主机和免费云服务器的使用体验。首先&#xff0c;让我们来看看三丰云的免费虚拟主机服务。三丰云的免费虚拟主机提供了稳定可靠的空间和带宽…

智能优化算法 | Matlab实现KOA开普勒优化算法(内含完整源码)

智能优化算法 | Matlab实现KOA开普勒优化算法(内含完整源码) 文章目录 智能优化算法 | Matlab实现KOA开普勒优化算法(内含完整源码)文章概述源码设计文章概述 智能优化算法 | Matlab实现KOA开普勒优化算法(内含完整源码) 源码设计 %% clear all clc N=25; % Number of s…

【Java】:方法重写、动态绑定和多态

目录 一个生动形象的例子 场景设定 1. 方法重写&#xff08;Method Overriding&#xff09; 2. 动态绑定&#xff08;Dynamic Binding&#xff09; 3. 多态&#xff08;Polymorphism&#xff09; 归纳关系&#xff1a; 重写 概念 条件 重写的示例 重载与重写的区别 …

【必看】Spring系列面试题

Spring Core Container, AOP, Data Access, Web... 基础 1. 简单介绍Spring 一款开源的轻量级 Java 开发框架&#xff0c;旨在提高开发人员的开发效率以及系统的可维护性。Spring 支持 IoC&#xff08;Inversion of Control:控制反转&#xff09; 和 AOP(Aspect-Oriented Pro…

【JVM类加载机制】深度剖析JVM类加载机制

深度剖析JVM类加载机制 前言类加载运行全过程loadClass的类加载过程 类加载器和双亲委派机制类加载器的类型类加载器的初始化过程双亲委派机制为什么要设置双亲委派机制&#xff1f;全盘负责委托机制自定义类加载器实例打破双亲委派机制Tomcat打破双亲委派机制Tomcat自定义加载…

[华为OD]BFS C卷 200 智能驾驶

题目&#xff1a; 有一辆汽车需要从m*n的地图的左上角(起点)开往地图的右下角(终点)&#xff0c;去往每一个地区都需 要消耗一定的油量&#xff0c;加油站可进行加油 请你计算汽车确保从起点到达终点时所需的最少初始油量说明&#xff1a; (1)智能汽车可以上下左右四个方向…

Python中的`__init__.py`文件:它的作用和重要性

引言 在Python的世界里&#xff0c;__init__.py文件是一个特殊的存在。对于初学者来说&#xff0c;它可能看起来有些神秘&#xff0c;但对于理解Python的包和模块系统来说&#xff0c;它起着至关重要的作用。本文将详细解释__init__.py文件的功能、它在Python项目中的作用&…

Threejs实现PMD模型眨眼说话等功能

上个章节通过加载PMD模型和VMD的动作播放MMD的动画&#xff0c;这节通过js控制让模型实现眨眼&#xff0c;说话。我们还是拿上个模型来操作&#xff0c;首先是创建好Threejs的场景&#xff0c;包括灯光&#xff0c;相机&#xff0c;渲染器等。 initScene(){this.scene new THR…

【Linux】Centos7安装部署unimrcp,搭建MRCP服务器

yum install libtool yum install libtool-ltdl-devel yum install libsofia-sip-ua find / -name libsofia-sip-ua.so.0 2>/dev/null # 设置环境变量&#xff1a;如果库文件存在但不在默认搜索路径中&#xff0c;你可以通过设置 LD_LIBRARY_PATH 环境变量来告诉系统在哪…

语音转文字服务的调用接口

语音转文字&#xff08;Speech-to-Text&#xff0c;STT&#xff09;技术允许将口语化的语音转换成书面文字。以下是一些提供语音转文字服务的调用接口及其特点。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1.讯飞开放平台语音转写…

[图解]实现领域驱动设计译文暴露的问题03

0 00:00:02,960 --> 00:00:04,940 前面我们讲了 1 00:00:05,260 --> 00:00:06,810 第①句话的 2 00:00:07,090 --> 00:00:09,750 第&#xff08;1&#xff09;个问题和第&#xff08;2&#xff09;个问题 3 00:00:13,920 --> 00:00:16,930 共享父类的对象&#…

【软件安装】vmware虚拟机安装完整教程(15.5版本)

安装包-百度网盘&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1zwYeRVdp1TM75JIctkWqVA?pwd6666 提取码&#xff1a;6666 1、去BIOS里修改设置开启虚拟化设备支持&#xff08;这一步必须要进行&#xff09; 网址&#xff1a;https://jingyan.baidu.com/article…