您的位置:首页 > 产品设计 > UI/UE

POJ 2299 Ultra-QuickSort (排序+数据离散化+求顺序数)

2013-09-27 14:57 441 查看
题意:给出一个序列,求至少需要用到多少次操作,才能将序列从小到大排序。

思路:

  数据量很大,n<500000,所以用冒泡模拟是超时的。 接下来就想到了求顺序数,总共需要交换的次数为每个数后面有多少个小于它的数的累加和。 如9 1 0 5 4,9后面比它小的有4个,1后面比它小的有1个,5后面比它小的有1个,总共为6个,即为答案。 求顺序数自然而然就是想到用树状数组。

  但是这里有一点,那就是整数是0~999999999,数据太大,开不了那么大的数组。 由于n最多只有500000,所以最多有50万个不同的数据,所以将数据离散化减少数据范围。

  用结构体存储数据最初的值value,以及一开始所在的位置idx,还有就是离散后的值ranks。 之后按值排序,从小到大开始赋值,最小的赋值1,然后依次递增。如果前后两个值相同,那么后一个赋前一个的ranks值。 最后再按照一开始所在的位置排序,从后往前赋值给数组a,a[i]的顺序数是前i-1个数中比a[i]小的个数,然后求累加和。 (按原来顺序赋值也可以,这样是求位于a[i]后面比a[i]小的个数的累加和,求的时候for循环从n开始递减)。

  注意最后输出要用long long!!!

  

  797ms,时间比别人长很多。。。

(不知道为什么,该代码在UVA10810 同样的一道题,竟然是WA。。。)

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std;
const int maxn=500000;
int a[maxn+10];  //存储离散后的数据序列,顺序是原来序列的顺序
int c[maxn+10];  //树状数组求和
int n;
struct Node{
int idx,ranks;  //一开始的位置,离散后的值
long long value;  //最初的值
}node[maxn+10];
//按value从小到大排序
bool cmp1(const Node & tmp1,const Node & tmp2){
return tmp1.value<tmp2.value;
}
//按idx从小到大排序
bool cmp2(const Node & tmp1,const Node & tmp2){
return tmp1.idx<tmp2.idx;
}
int lowbit(int x){
return x&(-x);
}
void update(int value,int i){
while(i<=n){
c[i]+=value;
i+=lowbit(i);
}
}
int sum(int i){
int sum=0;
while(i){
sum+=c[i];
i-=lowbit(i);
}
return sum;
}
int main()
{
long long data;
while(scanf("%d",&n)!=EOF){
if(n==0)
break;
memset(c,0,sizeof(c));
for(int i=0;i<n;i++){
scanf("%I64d",&data);
node[i].value=data;
node[i].idx=i;
}
sort(node,node+n,cmp1);

//将数据离散化
int num=1;
node[0].ranks=1;
for(int i=1;i<n;i++){
if(node[i].value==node[i-1].value){
node[i].ranks=node[i-1].ranks;
}
else{
num++;
node[i].ranks=num;
}
}
sort(node,node+n,cmp2);
for(int i=0;i<n;i++){
a[n-i]=node[i].ranks;
}
//求顺序数
long long ans=0;
for(int i=1;i<=n;i++){
ans+=sum(a[i]-1);
update(1,a[i]);
}
printf("%I64d\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: