【九度】题目1348:数组中的逆序对

来源:互联网 发布:掌握5.0数据库和模型 编辑:程序博客网 时间:2024/05/16 19:07
题目描述:
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
输入:
每个测试案例包括两行:
第一行包含一个整数n,表示数组中的元素个数。其中1 <= n <= 10^5。
第二行包含n个整数,每个数组均为int类型。
输出:
对应每个测试案例,输出一个整数,表示数组中的逆序对的总数。
样例输入:
47 5 6 4
样例输出:

5

分析:

题目限制时间是1秒,而基本操作复杂度超过10的9次方基本上就会超时,显然,不能采用暴力法。先考虑用分治法。

将数组分成前后两半部分,各半部分之内的逆序对数可以继续采用分治法递归求解,剩下了就是两半部分之间的逆序对数。问题转化为求两个数组,前一个数组里的一个数字和后一个数组里的一个数字构成的数对是逆序对的个数。当两个数组有序(降序)时,显然这个问题就可以在O(N)内求解。也就是说,在递归过程中,我们还需要额外处理,使得递归后的数组是降序,这其实就是二分排序。总的来说,时间复杂度为nlogn。

代码如下:

// 1348.cpp : 定义控制台应用程序的入口点。
//


//#include "stdafx.h"
#include<stdio.h>
#define N 100001
int num[N];
int buff[N];
long long func(int st,int en)
{
if(st>=en)
return 0;
if(st==en-1)
{
if(num[st]>num[en])
return 1;
else
{
//使返回数组有序
int tmp=num[en];
num[en]=num[st];
num[st]=tmp;
return 0;
}
}
int mediu=(st+en)/2;
long long t1=func(st,mediu);//前半部分内逆序对数
long long t2=func(mediu+1,en);//后半部分内逆序对数
int i=st,j=mediu+1;
long long t3=0;       
int z=j;
while(i<=mediu)//计算两部分间逆序对数
{
while(j<=en&&num[i]<=num[j])
{
z=j;
++j;
}
if(j==en+1)
{
t3+=0;
}
else
{
t3+=en-j+1;
}
++i;
j=z;   //z记录的是当前num[i]<=num[z]的最大的z,也就是在后半部分数组中,值不小于num[i]的最小的一个数,这个数之前的数必然不小于num[i+1]
}
//两有序部分的合并
i=st,j=mediu+1;
int k=st;
while(k<=mediu&&j<=en)
{
if(num[k]<num[j])
{
buff[i++]=num[j];
++j;
}
else
{
buff[i++]=num[k];
++k;
}
}
while(k<=mediu)
{
buff[i++]=num[k++];
}
while(j<=en)
{
buff[i++]=num[j++];
}
for(int t=st;t<=en;++t)
num[t]=buff[t];
return t1+t2+t3;
}
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
for(int i=0;i<n;++i)
scanf("%d",&num[i]);
long long ret=func(0,n-1);
printf("%lld\n",ret);
}
return 0;
}


0 0
原创粉丝点击