一本通1311 求逆序对(归并排序应用)

【题目描述】

给定一个序列a1,a2,,an,如果存在i<j并且ai>aj,那么我们称之为逆序对,求逆序对的数目。

【输入】

第一行为n,表示序列长度,接下来的n行,第i+1行表示序列中的第i个数。

【输出】

所有逆序对总数。

【输入样例】

4
3
2
3
2

【输出样例】

3


逆序对实际上可以用冒泡排序来求,一个数交换了几次就说明有几个逆序,但是这样实在是太慢了,时间复杂度O(n*n)
如果用归并,就可以排好序,然后找到第一组逆序,直接加上靠前的大数后面数的个数即可,比如:{3,4,7,9}和{1,5,8,10}
发现3要比1大,那么3后面的数自然都比1大,再加上3本身,所以就有mid-i+1个逆序了


代码:


#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
long long a[1000001],r[1000001];//根据取值范围定义数组(多打几个零不要在意,只要注意不要爆空间就行)
long long ans; //这里都用long long
void msort(long long s,long long t)
{
if(s == t) return;//如果只有一个数,自然不用排序,直接返回
long long mid = (s + t) / 2;
msort(s,mid);//虽然还没有开始写排序代码,但是可以直接进行递归(我也不太明白,要请教大佬)

msort(mid + 1,t);//归并排序名字由此得来,分开排序再合在一起
long long i = s,j = mid + 1,k = s;
while(i <= mid&&j <= t)//当满足前面的数在序列前半部分已经后面的数在序列后半部分时
{
if(a[i] <= a[j])//如果没有逆序,那么新数组存放较小的数(从小到大排序)
r[k ++] = a[i ++];
else
{
ans += mid - i + 1;//如果出现逆序,那么参考思路,直接套公式
r[k ++] = a[j ++];//还是把较小的数放进去
}
}
while(i <= mid)//如果有一边没有放完,那么放进去
r[k ++] = a[i ++];
while (j <= t)
r[k ++] = a[j ++];
for(long long i = s;i <= t;i ++)
a[i] = r[i];//将新数组里的数据再存回到原先的数组里去
}
int main()
{
long long n;
cin >> n;
for(long long i = 1;i <= n;i ++)
cin >> a[i];
msort(1,n);
cout << ans;
return 0;
}

posted @ 2020-03-29 23:07  徐明拯  阅读(581)  评论(0编辑  收藏  举报