逆序对的两种解法

题目描述

猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。
Update:数据已加强。

输入输出格式

输入格式:

第一行,一个数n,表示序列中有n个数。

第二行n个数,表示给定的序列。序列中每个数字不超过10^9109

输出格式:

给定序列中逆序对的数目。

输入输出样例

输入样例#1: 复制

6
5 4 2 6 3 1

输出样例#1: 复制

11

说明

对于25%的数据,n \leq 2500n≤2500

对于50%的数据,n \leq 4 \times 10^4n≤4×104。

对于所有数据,n \leq 5 \times 10^5n≤5×105

请使用较快的输入输出

应该不会n方过50万吧 by chen_zhe

其实解法不止两种,暴搜我就不说了,一定超时,所以我还是比较推崇下面两种解法:

一  归并排序

  我们知道归并排序是将一个数组不断的分程两部分,然后比较大小,暂存到另一数组,每两小部分比完就赋值到另一个数组中去,最后会得到最后的数组

为什么逆序对可以用归并排序来算呢,因为归并排序在排序的过程中进行比较大小的过程就是获取逆序对数量的过程

上代码:

#include<cstdio>
#include<iostream>
using namespace std;
int n,a[500010],c[500010];
long long ans;

void msort(int b,int e)//归并排序
{
    if(b==e)  
        return;
    int mid=(b+e)/2,i=b,j=mid+1,k=b;
    msort(b,mid),msort(mid+1,e);
    while(i<=mid&&j<=e)
        if(a[i]<=a[j])
            c[k++]=a[i++];
        else
            c[k++]=a[j++],ans+=mid-i+1;//统计答案
    while(i<=mid)
        c[k++]=a[i++];
    while(j<=e)
        c[k++]=a[j++];
    for(int l=b;l<=e;l++)
        a[l]=c[l];
} 

int main()
{
    scanf("%d",&n); 
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    msort(1,n);
    printf("%lld",ans);
    return 0;
}

因为我们在归并排序合并两个已排序的序列的时候,比如:

1  3   5   6   和    2   3   7   8

mid是4,然后如果当i停留在5和3比较的时候,发现5比3大,所以明显5之后的数字6也是比3大的,所以比三大的数字一共有mid-i+1=2个,所以循环反复就可以得出答案

二  树状数组:

树状数组具体我不解释了,可以参考:https://blog.csdn.net/Small_Orange_glory/article/details/81290634

这里的离散化很重要,体现了一个节约空间的方法:

比如我们输入六个  5 4 2 6 3 1

他的逆序对个数是11个

然后我们将他们的座位记录下来  1  2  3  4  5  6

我们将这1~6个数字按照对应的数字(上面的数组)从大到小排序的话,就是 4(6)  1(5)  2(4)  5(3)  3(2)  6(1)

#include<algorithm>
#include<cstdio>
using namespace std;
int n, m, c[40005] = { 0 }, a[40005] = { 0 }, b[40005] = { 0 };  //定义数组,b[]为读入的数组,a[]为要离散化后的数组,C[]为树状数组
int lowbit(int t)
{
	return t & (-t);
}
inline void Add(register int x)  //树状数组加入
{
	for (; x <= n; x += lowbit(x))
		c[x]++;  //因为这里只需要1,所以我就写出c[x]++了
}
inline int Sum(register int x)  //树状数组求和,同上面的sum(x)
{
	register int s = 0;  //计数的变量
	for (; x>0; x -= lowbit(x))  //累计
		s += c[x];
	return s;  //返回结果
}
bool cmp1(const int &x, const int &y)  //离散化需要用的,上面有讲
{
	if (b[x] == b[y])
		return x > y;
	return b[x]>b[y];
}
int main()
{
	long long int ans = 0;  //ans为最后的结果
	scanf_s("%d", &n);  //读入n
	for (register int i = 1; i <= n; i++)
	{
		scanf_s("%d", &b[i]);  //读入
		a[i] = i;  //顺便初始化一下a数组
	}
	sort(a + 1, a + n + 1, cmp1);  //给a数组排序,达到最终的效果
	for (register int i = 1; i <= n; i++)
	{
		Add(a[i]);  //依次加入树状数组
		ans += Sum(a[i] - 1);  //计算结果并累计
	}
	printf_s("%d", ans);  //输出ans
	return 0;  //返回
}

猜你喜欢

转载自blog.csdn.net/scwMason/article/details/86935658
今日推荐