逆序对问题、树状数组与离散化

news2024/9/22 4:18:37

目录

一、前言

二、逆序对问题

1、暴力法

2、归并排序法

3、树状数组与逆序对(最巧妙最好的方法)

(1)倒序

(2)正序

(3)离散化

三、例题

1、小朋友排队(lanqiaoOJ题号222)


一、前言

本文主要讲了逆序对问题、归并排序法、树状数组与离散化处理,并讨论了一道例题。

二、逆序对问题

【题目描述】

给定一个序列 A1, ......, An。若 i<j 且 Ai>Aj,则 <i,j> 就是为一个“逆序对"。请你写一个程序,在尽量短的时间内统计出”逆序对“的数目。

【输入格式】

第 1 行是整数 n(1≤n≤500000),接下来1行,n个整数。

【输出格式】

一个整数,为逆序对的数目。

【输入样例】

6

5 4 2 6 3 1

【输出样例】

11

1、暴力法

模拟:先检查第一个数 a1,把后面所有数跟它比较,如果发现有一个比 a1小,就是一个逆序对;再检查第二个数,第三个数……;直到最后一个数。

复杂度:O(n^2)。

本题 n 最大 10^5,暴力法 TLE

n=int(input())
a=list(map(int,input().split()))
res=0
for i in range(n):
    for j in range(i+1,n):
        if a[j]<a[i]:
            res+=1
print(res)

2、归并排序法

观察暴力法的执行过程,发现和交换排序很像。

能否用交换排序的升级版归并排序,来处理逆序对问题?

那就试一试!

归并排序:

(1)分解。把初始序列分成长度相同的左右两个子序列,然后把每个子序列再分成更小的两个子序列……,直到子序列只包含1个数。用递归实现。

(2)合并。归并2个有序的子序列。

复杂度:

对n个数进行归并排序:

(1)需要 logn 趟归并;

(2)在每一趟归并中,有很多次合并操作,一共需要 O(n) 次比较。

复杂度:O(nlogn)

【归并排序和逆序对】

观察归并排序的一次合并过程,发现能利用这个过程来记录逆序对。

(1)在子序列内部,元素都是有序的,不存在逆序对;逆序对只存在于不同的子序列之间。

(2)合并两个子序列时:

如果前一个子序列的元素比后面子序列的元素小,无逆序对。

如果前一个子序列的元素比后面子序列的元素大,有逆序对,且有 mid-(i-1) 个(why mid-i+1,看下面代码可以知道)

def merge(L,mid,R):
    global res
    i=L
    j=mid+1
    t=0
    while i<=mid and j<=R:
        if a[i]>a[j]:       #前面部分大于后面部分的数值,说明是逆序对
            b[t]=a[j]
            t+=1
            j+=1
            res=res+mid-i+1     #记录逆序对数量
        else:
            b[t]=a[i]
            t+=1
            i+=1
    #一个子序列中的数都处理完了,另一个还没有,把剩下的复制过来
    while i<=mid:
        b[t]=a[i]
        t+=1
        i+=1
    while j<=R:
        b[t]=a[j]
        t+=1
        j+=1
    for i in range(t):
        a[L+i]=b[i]         #把排好序的b[]复制回a[]
            

def merge_sort(L,R):        #归并分治,你还记不记得hhh,用sort太多了(doge)
    if L<R:
        mid=(L+R)//2    #平分成两个子序列
        merge_sort(L,mid)
        merge_sort(mid+1,R)
        merge(L,mid,R)

n=int(input())
a=list(map(int,input().split()))
b=[0]*n
res=0
merge_sort(0,n-1)
print(res)

代码通俗易懂,不再作过多的赘述。

3、树状数组与逆序对(最巧妙最好的方法)

  • 用树状数组求逆序对,是树状数组的巧妙应用,比其他方法都好。
  • 用树状数组解逆序对:把数字看成树状数组的下标
  • 例如序列 {5, 4, 2, 6, 3, 1},对应 a[5]、a[4]、a[2]、a[6]、a[3]、a[1]。
  • 每处理一个数字,树状数组的下标所对应的元素数值加一,统计前缀和,就是逆序对的数量。
  • 倒序或正序处理数据都行。

(1)倒序

用树状数组倒序处理数列,当前数字的前一个数的前缀和即为以该数为较大数的逆序对的个数。

例如 {5,4,2,6,3,1},倒序处理数宇:

数字1。把 a[1] 加一,计算 a[1] 前面的前缀和 sum(0),逆序对数量ans = ans + sum(0) = 0;

数字3。把 a[3] 加一,计算 a[3] 前面的前缀和 sum(2),逆序对数量ans = ans + sum(2) = 1;

数字6。把 a[6] 加一,计算 a[6] 前面的前缀和 sum(5),逆序对数量ans = ans + sum(5) = 1 + 2 = 3

(2)正序

当前已经处理的数字个数减掉当前数字的前缀和即为以该数为较小数的逆序对个数。

例如 {5,4,2,6,3,1},正序处理数字:

  • 数宇5。把 a[5] 加一,当前处理了 1 个数,ans = ans + (1-sum(5)) = 0;
  • 数字4。把 a[4] 加一,当前处理了 2 个数,ans = ans + (2-sum(4)) = 0+1 = 1;
  • 数宇2。把 a[2] 加一,ans = ans + (3-sum(2)) = 1+2 = 3;
  • 数字6。把 a[6] 加一,ans = ans + (4-sum(6)) = 3+0 = 3;
  • 等等

(3)离散化

  • 上面的处理方法 “把数字看成树状数组的下标” 有个问题,如果数字比较大,例如数字等于 10^9,那么树状数组的空问也要开到 10^9=1G,远远超过了题目限制的空间。
  • 用 “离散化” 这个小技巧能解决这个问题。
  • 离散化:把原来的数字,用它们的相对大小来替换原来的数值,而它们的顺序仍然不变。
  • 例:{1,20543,19,376,546007640},它们的相对大小是{1,4,2,3,5}。
  • 有多少个数字,离散化后的每个数字的大小就是多大。
  • 在用树状数组求解逆序对的题目中,离散化几乎是必须的
  • 本题需处理 500000 个数字,离散化之后树状数组大小只需 500000。

【离散化方法1:重复数字离散化后不一样】

a=[1,20543,19,376,546007640,19]
print(a)
b=sorted(a)
print(b)
for i in range(len(b)):
    k=a.index(b[i])
    a[k]=i+1                #a[a.index(b[i])] = i+1
print(a)

【离散化方法2:重复数字离散化后也一样】

def discretization (h):
    b=list(set(h))
    b.sort()
    for i in range(len(h)) :
        h[i] = b.index(h[i])+1
a = [1,20543,19,376,546007640,19]
print(a)
discretization(a)
print(a)

【逆序对:树状数组代码】

def lowbit(x):
    return x&-x

def update(x,d):
    while x<=n:
        tree[x]+=d
        x+=lowbit(x)

def sum(x):
    ans=0
    while x>0:
        ans+=tree[x]
        x-=lowbit(x)
    return ans

n=int(input())
a=[0]+list(map(int,input().split()))        #从a[1]开始
b=sorted(a)
for i in range(n+1):
    a[a.index(b[i])]=i+1
tree=[0]*(n+1)
res=0
for i in range(len(a)-1,0,-1):
    update(a[i],1)
    res+=sum(a[i]-1)
print(res)

三、例题

1、小朋友排队(lanqiaoOJ题号222)

【题目描述】

n 个小朋友站成一排。现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。每个小朋友都有一个不高兴的程度。开始的时候,所有小朋友的不高兴程度都是 0。如果某个小朋友第一次被要求交换,则他的不高兴程度增加 1,如果第二次要求他交换,则他的不高兴程度增加 2(即不高兴程度为3),依次类推。

当要求某个小朋友第 k 次交换时,他的不高兴程度增加 k。请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。

【输入格式】

输入的第一行包含一个整数 n,表示小朋友的个数。第二行包含 n 个整数 H1、H2、…、Hn,分别表示每个小朋友的身高。1<=n<=100000,0<=Hi<=1000000。

【输出格式】

输出一行,包含一个整数,表示小朋友的不高兴程度和的最小值。

每个小朋友要交换多少次?每一个小朋友最少的交换次数等于他左边比他高的人数加上右边比他矮的人数。

逆序对问题,包括2个逆序对:

(1)他左边比他高的人;

(2)右边比他矮的人。

用树状数组做两次逆序对,一次正序处理,一次倒序处理。

本题不需离散化。

def lowbit(x):
    return x&-x

def update(x,d):
    while x<=N:
        tree[x]+=d
        x+=lowbit(x)

def sum(x):
    ans=0
    while x>0:
        ans+=tree[x]
        x-=lowbit(x)
    return ans

N=1000010
n=int(input())
Hold=list(map(int,input().split()))
H=[0 for _ in range(N)]             #H是身高
for i in range(n):
    H[i+1] = Hold[i]+1

k=[0 for _ in range(N)]             #每个小朋友的最少交换次数.也是逆序对数量
tree=[0 for _ in range(N)]

for i in range(1,n+1):              #正序处理逆序对,右边矮的
    k[i]=sum(N-1)-sum(H[i])
    update(H[i],1)

tree=[0 for _ in range(N)]

for i in range(n,0,-1):              #倒序处理逆序对,左边高的
    k[i]+=sum(H[i]-1)
    update(H[i],1)

res=0
for i in range(1,n+1,1):
    res+=int((1+k[i])*k[i]/2)
print(res)

以上,逆序对问题、树状数组与离散化

祝好

 

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

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

相关文章

springBoot——SpringBoot 整合 Mybatis 流程

SpringBoot 整合持久层SpringBoot 整合 Mybatis 流程一、添加依赖1. pom.xml二、写配置文件1. application.yml三、写 Java 文件1. entity2. dao3. service4. controller5. mapper四、建立数据库五、测试SpringBoot 整合 Mybatis-Plus 流程一、添加依赖1. pom.xml二、写配置文件…

linux基本功系列之chattr命令

文章目录一. chattr命令介绍二. 语法格式及常用选项三. 参考案例3.1 给指定文件添加隐藏属性&#xff0c;阻止文件被修改&#xff1a;3.2 撤销i属性3.3 允许补充&#xff08;追加&#xff09;内容&#xff0c;无法覆盖/删除内容总结前言&#x1f680;&#x1f680;&#x1f680…

LabVIEW监控操纵杆,键盘或鼠标

LabVIEW监控操纵杆&#xff0c;键盘或鼠标在LabVIEW中从操纵杆/鼠标/键盘获取数据在LabVIEW中监控我的输入设备如何知道键盘上按了哪些键&#xff1f;用轮询的方式监控鼠标&#xff0c;键盘或操纵杆的动作。要在LabVIEW中监控这些设备&#xff0c;可以使用内置的输入设备采集VI…

【STM32】寄存器原理

如果我们想要深入去学习STM32单片机的存储原理和方式&#xff0c;就要花时间去了解STM32单片机有关寄存器的基本原理 单片机型号&#xff1a;正点原子STM32F103ZET6 目录 寄存器的定义 寄存器分类 存储器映射 寄存器映射 通过地址访问控制单元运作 通过定义宏来取代直接访…

C++ 哈希表查询_进入哈希函数结界的世界

1. 前言 哈希表或称为散列表&#xff0c;是一种常见的、使用频率非常高的数据存储方案。 哈希表属于抽象数据结构&#xff0c;需要开发者按哈希表数据结构的存储要求进行 API 定制&#xff0c;对于大部分高级语言而言&#xff0c;都会提供已经实现好的、可直接使用的 API&…

基于PythonOpenCv的视频图像处理

博主简介 博主是一名大二学生&#xff0c;主攻人工智能研究。感谢让我们在CSDN相遇&#xff0c;博主致力于在这里分享关于人工智能&#xff0c;c&#xff0c;Python&#xff0c;爬虫等方面知识的分享。 如果有需要的小伙伴可以关注博主&#xff0c;博主会继续更新的&#xff0c…

【教程】虚拟环境与Pytorch安装保姆级教学

【教程】虚拟环境与Pytorch安装保姆级教学NVIDIA驱动安装虚拟环境创建激活/退出相关库的安装Pycharm内设置虚拟环境Pytorch安装安装地址可能遇到的问题处理报错安装卡顿测试是否安装完成参考NVIDIA驱动安装 NVIDIA驱动可在官网进行安装&#xff1a;NVIDIA驱动官网 命令行输入…

一文上手决策树:从理论到实战

一、基础概念 决策树是一类极为常用的机器学习方法&#xff0c;尤其是在分类场景。决策树通过树形结构来递归地将样本分割到不同的叶子结点中去&#xff0c;并根据每个叶子结点中的样本构成对该结点中的样本进行分类。 我们可以从两个视角来理解决策树模型。 第一种视角是将…

Python副业技术总结,手把手教你用宝塔面板部署Django程序

前言 最近写了几个Django项目&#xff0c;写完以后怎么让对方测试成了问题&#xff0c;因为之前都是自己在本地写的练习项目&#xff0c;对于部署这一块很陌生&#xff0c;不知道怎么操作&#xff0c;内心很忐忑。没办法&#xff0c;只能硬着头皮上&#xff0c;一边百度&#…

17种编程语言实现排序算法-插入排序

开源地址 https://gitee.com/lblbc/simple-works/tree/master/sort/ 覆盖语言&#xff1a;C、C、C#、Java、Kotlin、Dart、Go、JavaScript(JS)、TypeScript(TS)、ArkTS、swift、PHP。 覆盖平台&#xff1a;安卓(Java、Kotlin)、iOS(SwiftUI)、Flutter(Dart)、Window桌面(C#)、…

ARP渗透与攻防(四)之WireShark截获用户数据

ARP-WireShark截获用户数据 系列文章 ARP渗透与攻防(一)之ARP原理 ARP渗透与攻防(二)之断网攻击 ARP渗透与攻防(三)之流量分析 1.WireShark工具介绍 wireshark的官方下载网站&#xff1a; WireShark wireshark是非常流行的网络封包分析软件&#xff0c;功能十分强大。可以…

PowerShell 学习笔记:压缩、解压缩文件

在自动构建的时候&#xff0c;最常用的就是压缩备份项目的源文件&#xff0c;PowerShell提供了相关命令。Compress-Archive&#xff08;压缩文件&#xff09;Compress-Archive[-Path] <String[]>[-DestinationPath] <String>[-CompressionLevel <String>][-P…

Crossplane - 比 Terraform 更先进的云基础架构管理平台?

&#x1f449;️URL: https://crossplane.io/ &#x1f4dd;Description: 将云基础架构和服务组成自定义平台 API 简介 在 11 月的 KCD 上海现场&#xff0c;听了一场阿里云的工程师关于他们自己的多云基础架构管理工具的介绍&#xff0c;前边的引言部分有介绍到 Terraform&am…

连续系统的数字PID控制仿真-3

在连续系统的数字PID控制仿真-2的基础上&#xff0c;利用S函数实现PID离散控制器的Simulink仿真。在S函数中&#xff0c;采用初始化函数、更新函数和输出函数&#xff0c;即 mdlInitializeSizes函数、mdIUpdates函数和mdlOutputs函数。在初始化中采用sizes 结构&#xff0c;选择…

什么是GRACE CPU --- Grace CPU架构详解

深入详解GRACE CPU架构 NVIDIA Grace CPU 是 NVIDIA 开发的第一款数据中心 CPU。 通过将 NVIDIA 专业知识与 Arm 处理器、片上结构、片上系统 (SoC) 设计和弹性高带宽低功耗内存技术相结合&#xff0c;NVIDIA Grace CPU 从头开始构建&#xff0c;以创建世界上第一个超级芯片 用…

Spring控制反转(IoC)和依赖注入(DI)

Spring官网&#xff1a;spring.io1.spring 2.SprinMVC 3.Maven高级 4.SpringBoot 5.MyBatisPlus为什么要学Spring?简化开发&#xff0c;降低企业级开发的复杂度框架整合&#xff0c;高效整合其他技术&#xff0c;提高企业级应用开发与运行效率Spring 系统架构IOC&#xff08;I…

分享132个ASP源码,总有一款适合您

ASP源码 分享132个ASP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 132个ASP源码下载链接&#xff1a;https://pan.baidu.com/s/1bk2hftqR5NTdUIT2zvmbiw?pwdke5x 提取码&#x…

离散数学与组合数学-04图论

文章目录离散数学与组合数学-04图论4.1 图的引入4.1.1 图的示例4.1.2 无序对和无序积4.1.3 图的定义4.2 图的表示4.2.1 集合表示和图形表示4.2.2 矩阵表示法4.2.3 邻接点与邻接边4.3 图的分类4.3.1 按边的方向分类4.3.2 按平行边分类4.3.3 按权值分类4.3.4 综合分类方法4.4 图论…

干货 | 移动互联网应用程序(APP)个人信息安全自我评测工具

以下内容整理自清华大学《数智安全与标准化》课程大作业期末报告同学的汇报内容。第一部分&#xff1a;研究背景概述截止今年6月&#xff0c;我国已经有APP 232万款&#xff0c;手机网民达到10.47亿&#xff0c;在APP中大规模的个人信息收集和使用成为常态&#xff0c;个人信息…

【算法题】1828. 统计一个圆中点的数目

插&#xff1a; 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 坚持不懈&#xff0c;越努力越幸运&#xff0c;大家一起学习鸭~~~ 题目&#xff1a; 给你一个数组 points &#xff0c;…