788. 逆序对的数量

news/2024/5/17 17:26:55 标签: c++, 归并排序, 排序, 算法, 数据结构

给定一个长度为n的整数数列,请你计算数列中的逆序对的数量。

逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i < j 且 a[i] > a[j],则其为一个逆序对;否则不是。

输入格式

第一行包含整数n,表示数列的长度。

第二行包含 n 个整数,表示整个数列。

输出格式

输出一个整数,表示逆序对的个数。

数据范围

1≤n≤100000

输入样例:

6
2 3 4 5 6 1

输出样例:

5

题解:

#include<iostream>

using namespace std;

const int N = 1e6 + 10;
int q[N], temp[N];
long ans;

void merge_sort(int q[], int l, int r){
	//递归终止条件
    if (l >= r) return;
    //确定分界点
    int mid = l + r >> 1;
    //递归排序
    merge_sort(q, l, mid);
    merge_sort(q, mid + 1, r);
    //归并--->合二为一
    int k = 0, i = l, j = mid + 1;
    while (i <= mid && j <= r){
        if (q[i] <= q[j]) temp[k++] = q[i++];
        else{
            ans =  ans + mid - i + 1;
            temp[k++] = q[j++];
        } 
    }
    			//扫尾
    while (i <= mid) temp[k++] = q[i++];
    while (j <= r) temp[k++] = q[j++];
    			//把归并后的序列复制到原序列中
    for (i = l, k = 0; i <= r; i++, k++) q[i] = temp[k];
}

int main(){
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) scanf("%d", &q[i]);
    merge_sort(q, 0, n - 1);
    printf("%ld", ans);
    
    return 0;
}

思路:

一看到这个题我就感觉似曾相识,知道需要使用排序>归并排序的思想来做,于是特地整理了一遍,见博客排序>归并排序

排序>归并排序的归并步骤时,可以顺便统计逆序对的个数。
在这里插入图片描述
假设正在归并上面的数组,左侧的2,3,6,8和右侧的1,4,5,7已经排好序了,左侧和右侧内部都没有逆序对,而从左侧取一个数,从右侧取一个数,则有可能形成逆序对。

例如,开始左侧拿出2,右侧拿出1,可知2>1,形成了逆序对。此时逆序对只是加1吗?并不是,因为2右边的数都是大于2的,所以可以判断左边的数和右边的1可以形成4对逆序对((2,1)、(3,1)、(6,1)、(8,1))。

接下来比24,不会形成逆序对。再比34,不会形成逆序对。

当比较到64的时候,形成了逆序对,个数为2((6,4)、(8,4))。

在这里插入图片描述
归纳一下,也就是在归并的时候,如果右侧的元素小于左侧的元素,这个时候开始统计逆序对就行了,如果左侧的索引为i,左侧的末尾元素的索引为mid,逆序对个数就为mid - i + 1

这样并没有结束,前面的假设是左侧和右侧是有序的,事实上并不是,左侧和右侧也进行了归并的过程才能变得有序,而在归并过程中,也能计算出逆序对的个数。

所以:

总的逆序对的个数=左侧归并时求得的逆序对个数 + 右侧归并时求得的逆序对个数 + 对整体进行归并时的逆序对个数。

可能会怀疑这三种情况会有重复,但是并没有。左侧归并找到的逆序对相当于从左侧数组中取2个数,而整体归并的时候是分别从左右数组中取1个数,不可能发生重复!

思路转至链接。

答案范围:
当我们的输入样例是一个降序数组5 4 3 2 1

逆序对的数量ans

=n−1+n−2+n−3+…+1=n−1+n−2+n−3+…+1
=n∗(n−1)/2=n∗(n−1)/2
n=1e5n=1e5
ans=5e9 > INT_MAX=1e9

所以

reslong long类型


http://www.niftyadmin.cn/n/765611.html

相关文章

UML类图关系(泛化 、继承、实现、依赖、关联、聚合、组合)

一、六大关系 继承/泛化&#xff08;Generalization&#xff09;&#xff1a; 指的是一个类&#xff08;称为子类、子接口&#xff09;继承另外的一个类&#xff08;称为父类、父接口&#xff09;的功能&#xff0c;并可以增加它自己的新功能的能力&#xff0c;继承是类与类或…

1053 住房空置率 (20 分)

在不打扰居民的前提下&#xff0c;统计住房空置率的一种方法是根据每户用电量的连续变化规律进行判断。判断方法如下&#xff1a; 在观察期内&#xff0c;若存在超过一半的日子用电量低于某给定的阈值 e&#xff0c;则该住房为“可能空置”&#xff1b;若观察期超过某给定阈值…

※1054 求平均值 (20 分)

本题的基本要求非常简单&#xff1a;给定 N 个实数&#xff0c;计算它们的平均值。但复杂的是有些输入数据可能是非法的。一个“合法”的输入是 [−1000,1000] 区间内的实数&#xff0c;并且最多精确到小数点后 2 位。当你计算平均值的时候&#xff0c;不能把那些非法的数据算在…

1055 集体照 (25 分)

拍集体照时队形很重要&#xff0c;这里对给定的 N 个人 K 排的队形设计排队规则如下&#xff1a; 每排人数为 N/K&#xff08;向下取整&#xff09;&#xff0c;多出来的人全部站在最后一排&#xff1b;后排所有人的个子都不比前排任何人矮&#xff1b;每排中最高者站中间&…

1056 组合数的和 (15 分)

给定 N 个非 0 的个位数字&#xff0c;用其中任意 2 个数字都可以组合成 1 个 2 位的数字。要求所有可能组合出来的 2 位数字的和。例如给定 2、5、8&#xff0c;则可以组合出&#xff1a;25、28、52、58、82、85&#xff0c;它们的和为330。 输入格式&#xff1a; 输入在一行…

※1057 数零壹 (20 分)

给定一串长度不超过 10^​5的字符串&#xff0c;本题要求你将其中所有英文字母的序号&#xff08;字母 a-z 对应序号 1-26&#xff0c;不分大小写&#xff09;相加&#xff0c;得到整数 N&#xff0c;然后再分析一下 N 的二进制表示中有多少 0、多少 1。例如给定字符串 PAT (Ba…

1058 选择题 (20 分)

批改多选题是比较麻烦的事情&#xff0c;本题就请你写个程序帮助老师批改多选题&#xff0c;并且指出哪道题错的人最多。 输入格式&#xff1a; 输入在第一行给出两个正整数 N&#xff08;≤ 1000&#xff09;和 M&#xff08;≤ 100&#xff09;&#xff0c;分别是学生人数和…

※1059 C语言竞赛 (20 分)

C 语言竞赛是浙江大学计算机学院主持的一个欢乐的竞赛。既然竞赛主旨是为了好玩&#xff0c;颁奖规则也就制定得很滑稽&#xff1a; 0、冠军将赢得一份“神秘大奖”&#xff08;比如很巨大的一本学生研究论文集……&#xff09;。1、排名为素数的学生将赢得最好的奖品 —— 小…