【题目链接】
ybt 1311:【例2.5】求逆序对
ybt 1237:求排列的逆序数
OpenJudge NOI 2.4 7622:求排列的逆序数
洛谷 P1908 逆序对
ybt 1311,1237 OpenJudge 2.4 7622几题输入数据的最大个数为
1
0
5
10^5
105
洛谷 P1908 输入数据的最大个数为
5
∗
1
0
5
5*10^5
5∗105
【题目考点】
1. 归并排序 求逆序对
【解题思路】
如果直接写两层循环寻找逆序对,时间复杂度是
O
(
n
2
)
O(n^2)
O(n2),该题问题规模为
1
0
5
10^5
105,会超时。
可以用归并排序解决逆序对问题
例:某一次归并排序,两个数组分别为a1,a2,临时数组为t,逆序对数量s:
a1:1 6 8 a2:2 3 7 t: s:0
第一步:a1的1进入到临时数组t中,1和a2中的数字不产生逆序对
a1:6 8 a2:2 3 7 t:1 s:0
第二步:a2的2比a1的6小,2进入t中。2和a1中的所有元素都形成逆序对,逆序对数量增加2
a1:6 8 a2:3 7 t:1 2 s:2
第三步:a2的3比a1的6小,3进入t中。3和a1中的所有元素都形成逆序对,逆序对数量增加2
a1:6 8 a2:7 t:1 2 3 s:4
第四步:a1的6比a2的7小,6进入t中。不产生逆序对
a1:8 a2:7 t:1 2 3 6 s:4
第五步:a2的7比a1的8小,7进入t中。产生逆序对1个
a1:8 a2: t:1 2 3 6 7 s:5
最后把8 填到t中
a1: a2: t:1 2 3 6 7 8 s:5
此次归并一共找到5个逆序对
总结规律:在合并有序序列时,当a2数组的最小值比a1数组最小值小的时候,逆序对增加的数量为:此时a1数组剩余元素的个数。其他时刻,逆序对不增加。
根据这一原理编写程序,归并排序的时间复杂度是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),可以解决规模为
1
0
5
10^5
105的问题
n个元素,当整个数组是严格降序序列时,有最多的逆序对:
第1元素和后面n-1个元素构成n-1个逆序对;
第2元素和后面n-2个元素构成n-2个逆序对;
。。。
第n-1元素和第n元素构成1个逆序对。
共有逆序对:
n
−
1
+
n
−
2
+
.
.
.
+
1
=
n
(
n
−
1
)
2
n-1+n-2+...+1 = \frac{n(n-1)}{2}
n−1+n−2+...+1=2n(n−1)
当n为
1
0
5
10^5
105时,逆序对最多有约为
1
0
10
10^{10}
1010个,超出了int可以表示的范围,所以保存逆序对的变量应该设为long long
【题解代码】
解法1:
ybt 1311,1237 OpenJudge 2.4 7622几题 数组长度N可以设为100005
洛谷 P1908 数组长度N必须设为500005
#include<bits/stdc++.h>
using namespace std;
#define N 500005//数组长度
int a[N], t[N];
long long ct;//逆序数
void mergeSort(int l, int r)
{
if(l >= r)
return;
int mid = (l + r)/2;
mergeSort(l, mid);
mergeSort(mid+1, r);
int ti = l, li = l, ri = mid+1;//li第一个段数组中的下标 ri第二段数组中的下标 ti临时数组中的下标
while(li <= mid && ri <= r)
{
if(a[li] <= a[ri])
t[ti++] = a[li++];
else//如果a[ri] < a[li] 此时a[ri]与左侧数组剩余的mid-li+1个数字构成逆序对
{
ct += mid-li+1;
t[ti++] = a[ri++];
}
}
while(li <= mid)
t[ti++] = a[li++];
while(ri <= r)
t[ti++] = a[ri++];
for(int i = l; i <= r; ++i)
a[i] = t[i];
}
int main()
{
int n;
cin >> n;
for(int i = 1; i <= n; ++i)
cin >> a[i];
mergeSort(1, n);
cout << ct;
return 0;
}