一本通 1311:【例2.5】求逆序对 ,归并排序

news/2024/5/19 21:14:05 标签: 排序算法, 算法, 快速排序

本题没有什么难度,一个模板题。之所以要写这个题解,是因为我自己第一次提交的时候,竟然没有 AC。老子立马懵逼了。后面耐心的分析了一下数据,才发现自己的错误在哪里。透剧一下,掉到数据越界的深坑了。所以记录一下。

题目
题目链接
一本通OJ:http://ybt.ssoier.cn:8088/problem_show.php?pid=1311。

OJ:http://47.110.135.197/problem.php?id=4134。

 

分析
一个标准求逆序对模板题。

逆序对
设 A 为一个有 n 个数字的有序集(n > 1),其中所有数字各不相同。如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 <A[i], A[j]> 这个有序对称为 A 的一个逆序对,也称作逆序数。

例如,数组(3,1,4,5,2)的逆序对有(3, 1),(3, 2),(4, 2),(5, 2),共 4 个逆序对。

算法思路
求数组的逆序对就是对数组进行排序,在排序过程中记录逆序的数量即可。这样我们需要使用稳定算法>排序算法,比如冒泡排序、插入排序和归并排序。

数据范围分析
1、从题目中,我们可以知道 n 的范围是 [1, 10^5],这样我们就知道所有 O(n^2) 复杂度的算法>排序算法肯定是 TLE,也就是说冒泡排序和插入排序不能使用,只有是要归并排序。

2、假设最坏的情况,也就是这 10^5 个数据全部是倒序,那么逆序对应该是 。

这个数据什么意思?我们来回忆一下 C++ 数据类型 unsigned int 的最大范围是 4,294,967,295,也就是 ,说明 unsigned int 已经容纳不下这个数据。太太太坑爹了。

归并排序求逆序对
这个部分是分析的核心。首先我们用一个样例数据来说明,使用归并排序。假设我们有一个数列 [5 4 2 6 3 1],使用归并排序来看一下有几个逆序对。

第一步:二分,如下图所示。逆序对数量 = 0。

第二步:第四层 5 和 4 合并:i=l=1; r=2; mid=(l+r)/2=1; j=mid+1=2; 因为 5>4,逆序对数量+mid-i+1=1。b数组:[4 5 0 0 0 0],j+1>r;退出;a 数组 [4 5 2 6 3 1]。如下图所示。

第三步:第四层 6 和 3 合并:i=l=4; r=5; mid=(l+r)/2=4; j=mid+1=5; 因为 6>3,逆序对数量+mid-i+1=2。b数组:[4 5 0 3 6 0],j+1>r;退出;a 数组 [4 5 2 6 3 1]。如下图所示。

第四步:第三层 4,5 和 2 合并:i=l=1; r=3; mid=(l+r)/2=2; j=mid+1=3; 因为 4>2,逆序对数量+mid-i+1=4。b数组:[2 4 5 3 6 0],j+1>r;退出;a 数组 [2 4 5 3 6 1]。如下图所示。

第五步:第三层 3,6 和 1 合并:i=l=4; r=6; mid=(l+r)/2=5; j=mid+1=6; 因为 3>1,逆序对数量+mid-i+1=6。b数组:[2 4 5 1 3 6],j+1>r;退出;a 数组 [2 4 5 1 3 6]。如下图所示。

第六步:第二层 2,4,5 和 1,3,6 合并:i=l=1; r=6; mid=(l+r)/2=3; j=mid+1=4; 因为 2>1,逆序对数量+mid-i+1=9。b数组:[1 2 4 5 3 6],j+1;对应 2<3,b数组:[1 2 4 5 3 6],i+1;对应 4>3,逆序对数量+mid-i+1=11,b数组:[1 2 3 4 5 6]。j+1;对应 4<6,b数组:[1 2 3 4 5 6];i+1,5<6,b数组:[1 2 3 4 5 6];i+1>mid,退出,a 数组 [1 2 3 4 5 6],有序。如下图所示。

从上面的分析可以看出,使用归并排序求逆序对,最核心的是下面这句话

ans+=mid-i+1;

//http://ybt.ssoier.cn:8088/problem_show.php?pid=1311
//求逆序对
#include <bits/stdc++.h>
using namespace std;
 
const int MAXN = 1e6+4;
int nums[MAXN] = {};
int tmps[MAXN];
long long ans = 0;
 
//使用归并排序
void merge(int l, int mid, int r) {
    int i=l;
    int j=mid+1;
    int k=l;
    while (i<=mid && j<=r) {
        if (nums[i]>nums[j]) {
            tmps[k++]=nums[j++];
            ans+=mid-i+1;
        } else {
            tmps[k++]=nums[i++];
        }
    }
 
    while (i<=mid) {
        tmps[k++]=nums[i++];
    }
    while (j<=r) {
        tmps[k++]=nums[j++];
    }
    for (i=l; i<=r; i++) {
        nums[i]=tmps[i];
    }
}
 
void mergeSort(int l, int r) {
    int mid;
    if (l < r) {
        mid = l+((r-l)>>1);
        mergeSort(l, mid);
        mergeSort(mid+1, r);
        merge(l, mid, r);
    }
}
 
int main() {
    int n;
    cin >> n;
    int i;
    for (i=0; i<n; i++) {
        cin >> nums[i];
    }
 
    mergeSort(0, n-1);
 
    cout << ans << endl;
 
    return 0;
}


————————————————
版权声明:本文为CSDN博主「努力的老周」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/justidle/article/details/104565227

 


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

相关文章

重新学.Net[三]——部署和加载

托管程序的部署是以程序集&#xff08;Assembly&#xff09;为基本单元的。前面说过&#xff0c;一个程序集包含若干个托管模块&#xff08;exe或dll&#xff09;。但通常&#xff0c;一个程序集只包含一个dll或exe&#xff08;在vs中也仅支持这种&#xff09;。为了生成含多个…

一本通 1311:【例2.5】求逆序对 ,两种解法(归并、线段树)

引用洛谷P1908 我会说明树状数组与归并排序两种解法&#xff0c;线段树解法与树状数组类似 ① 归并排序解法 首先你需要知道什么是归并排序。然后&#xff0c;我们可以这样想&#xff1a; 如果我们想要将一个序列排成从小到大有序的&#xff0c;那么每次划分后合并时左右子区…

归并排序 学习 【标程】

爸爸写的归并&#xff0c;比孩子还要落后很多了&#xff08;孩子学到dp和基础的图&#xff09;。。 不过也要每天努力一点点&#xff0c;争取跟上孩子。 #include<bits/stdc.h> using namespace std;int a[10005]; int b[10005];// test merge /* a[0] 3;a[1] 5;a[2] …

一致的数据访问技术ADO/OLE DB

Microsoft新近推出的UDA(Universal Data Access,一致数据访 问技术)为关系型或非关系型数据访问提供了一致的访问接口,为企业 级Intranet应用多层软件结构提供了数据接口标准。一致数据访问包 括两层软件接口,分别为ADO(Active Data Object)和OLED B,对应于 不同层次的应用开发…

一本通 1232 Crossing River 尚贤写的

一本通 1232 Crossing River 尚贤写的 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define F(i, n, m) for (int i n; i < m; i) #define UF(i, n, m) for (int i n; i > m; --i) #define SIZE (int)1e…

一本通 1237:求排列的逆序数 1311:【例2.5】求逆序对 ,爸爸写的

爸爸写的 #include<bits/stdc.h> using namespace std;int a[100005]; int b[100005]; long long ans 0;void m(int l,int r){int p1 l ,mid (rl)/2 , p2 mid 1, p l; while( p1<mid && p2<r ){if(a[p1]<a[p2]){b[p] a[p1];}else{b[p] a[p2];…

回到学校的感受

回到学校的感受&#xff0c;只有一个字累。这次来学校带的行头来多了&#xff0c;我看了看总行李的公斤数大约在100斤左右&#xff0c;体积有两个立方米。我自己左手两箱羊肉汤&#xff0c;右手提着大的扁丝带&#xff0c;内含一个大的手提包&#xff0c;塑料袋5个&#xff0c;…

一本通 1176:谁考了第k名 冒泡 (爸爸写的)

1176&#xff1a;谁考了第k名 时间限制: 1000 ms 内存限制: 65536 KB 提交数: 21406 通过数: 9157 【题目描述】 在一次考试中&#xff0c;每个学生的成绩都不相同&#xff0c;现知道了每个学生的学号和成绩&#xff0c;求考第k名学生的学号和成绩。 【输入】 …