归并排序求逆序对
什么是逆序对:
设 A 为一个有 n 个数字的有序集 (n>1),其中所有数字各不相同。 如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 <A[i], A[j]> 这个有序对称为 A 的一个逆序对,也称作逆序数。
如果还是不懂请点这里
怎么求逆序对:
求逆序对就需要先介绍一种排序方法:
SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。归并排序:
归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治策略分治法将问题分成一些小的问题然后递归求解.
举个例子:
输入n个数,要求从大到小排序:
【思路】:利用分治发(二分),从中间分开,再把左右依次分开,始终让小区间内的数从小到大,那么这是分治的思想(分而治之)
图解(来自dreamcatcher-cs的博客):
让后利用一个新的数组把数据放过去,让后再放回来
代码:

//代码摘自其他博客(懒得打了) /* * 将一个数组中的两个相邻有序区间合并成一个 * * 参数说明: * a -- 包含两个有序区间的数组 * start -- 第1个有序区间的起始地址。 * mid -- 第1个有序区间的结束地址。也是第2个有序区间的起始地址。 * end -- 第2个有序区间的结束地址。 */ void merge(int a[], int start, int mid, int end) { int *tmp = (int *)malloc((end-start+1)*sizeof(int)); // tmp是汇总2个有序区的临时区域 int i = start; // 第1个有序区的索引 int j = mid + 1; // 第2个有序区的索引 int k = 0; // 临时区域的索引 while(i <= mid && j <= end) { if (a[i] <= a[j]) tmp[k++] = a[i++]; else tmp[k++] = a[j++]; } while(i <= mid) tmp[k++] = a[i++]; while(j <= end) tmp[k++] = a[j++]; // 将排序后的元素,全部都整合到数组a中。 for (i = 0; i < k; i++) a[start + i] = tmp[i]; free(tmp); } /* * 归并排序(从上往下) * * 参数说明: * a -- 待排序的数组 * start -- 数组的起始地址 * endi -- 数组的结束地址 */ void merge_sort_up2down(int a[], int start, int end) { if(a==NULL || start >= end) return ; int mid = (end + start)/2; merge_sort_up2down(a, start, mid); // 递归排序a[start...mid] merge_sort_up2down(a, mid+1, end); // 递归排序a[mid+1...end] // a[start...mid] 和 a[mid...end]是两个有序空间, // 将它们排序成一个有序空间a[start...end] merge(a, start, mid, end); }点击展开
接下来终于到逆序对了:
放两个题目:
【例7.7】光荣的梦想
【题目描述】
Prince对他在这片大陆上维护的秩序感到满意,于是决定启程离开艾泽拉斯。在他动身之前,Prince决定赋予King_Bette最强大的能量以守护世界、保卫这里的平衡与和谐。在那个时代,平衡是个梦想。因为有很多奇异的物种拥有各种不稳定的能量,平衡瞬间即被打破。KB决定求助于你,帮助他完成这个梦想。
一串数列即表示一个世界的状态。
平衡是指这串数列以升序排列。而从一串无序数列到有序数列需要通过交换数列中的元素来实现。KB的能量只能交换相邻两个数字。他想知道他最少需要交换几次就能使数列有序。
【输入】
第一行为数列中数的个数n,第二行为n ≤ 10000个数。表示当前数列的状态。
【输出】
输出一个整数,表示最少需要交换几次能达到平衡状态。
【输入样例】
4 2 1 4 3
【输出样例】
2
一看就是用归并排序求逆序对不想解释了(我太懒了)
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<queue> #include<stack> #include<vector> #include<map> #include<string> #include<cstring> using namespace std; const int maxn=999999999; const int minn=-999999999; int b[500005];//暂时存储用 inline int read() { char c = getchar(); int x = 0, f = 1; while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } int n,a[500005],ans; void my_sort(int left,int right) { int mid=(left+right)/2; if(left>=right) { return ; } my_sort(left,mid); my_sort(mid+1,right); int i=left,j=mid+1,n=mid,m=right,k=0; while(i<=n&&j<=m) if(a[i]>a[j]) { ans+=n-i+1; b[k++]=a[j++]; } else b[k++]=a[i++]; while(i<=n) b[k++]=a[i++]; while(j<=m) b[k++]=a[j++]; for(i=0; i<k; i++) a[left+i]=b[i]; } int main() { cin>>n; for(int i=1; i<=n; ++i) { cin>>a[i]; } my_sort(1,n); cout<<ans; return 0; }
P1908 逆序对
题目描述
猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。
Update:数据已加强。
输入输出格式
输入格式:
第一行,一个数n,表示序列中有n个数。
第二行n个数,表示给定的序列。序列中每个数字不超过10^9
输出格式:
给定序列中逆序对的数目。
输入输出样例
输入样例#1:6 5 4 2 6 3 1输出样例#1:
11
注意要开long long
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<queue> #include<stack> #include<vector> #include<map> #include<string> #include<cstring> using namespace std; const int maxn=999999999; const int minn=-999999999; long long b[500005];//暂时存储用 inline int read() { char c = getchar(); int x = 0, f = 1; while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } long long n,a[500005],ans; void my_sort(int left,int right) { int mid=(left+right)/2; if(left>=right) { return ; } my_sort(left,mid); my_sort(mid+1,right); int i=left,j=mid+1,n=mid,m=right,k=0; while(i<=n&&j<=m) if(a[i]>a[j]) { ans+=n-i+1; b[k++]=a[j++]; } else b[k++]=a[i++]; while(i<=n) b[k++]=a[i++]; while(j<=m) b[k++]=a[j++]; for(i=0; i<k; i++) a[left+i]=b[i]; } int main() { cin>>n; for(int i=1; i<=n; ++i) { cin>>a[i]; } my_sort(1,n); cout<<ans; return 0; }
