B - Ultra-QuickSort POJ 2299
Time Limit: 7000 MS Memory Limit: 65536 KB
64-bit integer IO format: %I64d , %I64u Java class name: Main
[Submit] [Status]
Description
In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swapping two adjacent sequence elements until the sequence is sorted in ascending order. For the input sequence
9 1 0 5 4 ,
Ultra-QuickSort produces the output
0 1 4 5 9 .
Your task is to determine how many swap operations Ultra-QuickSort needs to perform in order to sort a given input sequence.
Input
The input contains several test cases. Every test case begins with a line that contains a single integer n < 500,000 – the length of the input sequence. Each of the the following n lines contains a single integer 0 ≤ a[i] ≤ 999,999,999, the i-th input sequence element. Input is terminated by a sequence of length n = 0. This sequence must not be processed.
Output
For every input sequence, your program prints a single line containing an integer number op, the minimum number of swap operations necessary to sort the given input sequence.
Sample Input
5
9
1
0
5
4
3
1
2
3
0
Sample Output
6
0
看这数据量,最大数接近十亿,然而时间要求是一秒,普通冒泡肯定是不行了。
事实上可以选用归并排序求逆序对,或者树状数组求逆序对,这里我采用的是树状数组,因为归并排序还没学会。。。
树状数组略解http://blog.csdn.net/qq_32680617/article/details/50867705
我们知道,树状数组的大小就是数据范围的大小。而题中需要排序的数据范围是一到十亿,树状数组显然不可能开十亿那么大,注意到实际最多输入五十万个数,所以我们可以通过数据的离散化,使数据大小和数据编号产生一一对应的关系,这样只需开五十万的数组就能表示十亿的数字。
关于离散化:
当数据只与它们之间的相对大小有关系,而与数据具体大小无关时,可以进行离散化
把无限空间中无限的个体映射到有限的空间中去,以此提高算法的时空效率。
离散化实现
设有4个数:
1234567、123456789、12345678、123456
排序:123456<1234567<12345678<123456789
=> 1 < 2 < 3 < 4
那么这4个数可以表示成:2、4、3、1
这里用相对大小替代了原本很大的数据
本题中利用一个结构体实现数据的离散化
struct Node
{
int num;//记录数字
int point;//记录数字对应下标
} node[N]; //接收数据
然后对结构体排序
bool cmp(Node x,Node y)//离散化前需要先排序
{
return x.num<y.num;//按照数字升序排列
}
sort(node+1,node+n+1,cmp);//排序然后离散化
此时数据和结构体的下标就是一一对应的关系
再将这种关系存储到映射数组map中
for(int i=1; i<=n; i++)
map[node[i].point]=i;//数据离散化
此时就相当于数据的相对大小都存储在映射数组map中,对于求逆序对数来说,这和存储真实数据大小是没有区别的。
接下来通过树状数组求逆序对数
数据不是很大时,直接将数据插入到树状数组中,数据很大时,离散化之后,将离散化的数据插入到树状数组中(例如本题,就需要离散化后插入)
每插入一个数,就计算它前面比它小的数的个数。
设i为已插入数的个数,那么此时的逆序对就是i-sum(map[i]),其中sum函数在下面的代码中有注释,是用来返回比i小的数的个数。
下面是具体代码实现
#include<stdio.h>
#include<string.h>
#include<string>
#include<stack>
#include<queue>
#include<math.h>
#include<limits.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=500005;
int map[N];//映射数组,用于数据离散化
int c[N];//树状数组
int n;//数字的数量
struct Node
{
int num;//记录数字
int point;//记录数字对应下标
} node[N]; //接收数据
bool cmp(Node x,Node y)//离散化前需要先排序
{
return x.num<y.num;//按照数字升序排列
}
int lowbit(int i)
{
return i&(-i);//返回i的二进制最后一位1的权值
}
void change(int i)//更新树状数组
{
//每插入一个数,就计算前面比这个数小的个数,并存在树状数组中
while(i<=n)
{
c[i]=c[i]+1;
i=i+lowbit(i);
}
}
int sum(int i)//返回比i小的数的个数
{
int sum=0;
while(i>0)
{
sum=sum+c[i];
i=i-lowbit(i);
}
return sum;
}
int main()
{
while(~scanf("%d",&n)&&n)
{
for(int i=1; i<=n; i++)
{
scanf("%d",&node[i].num);//接收数字
node[i].point=i;//记录数字对应的下标
}
sort(node+1,node+n+1,cmp);//排序然后离散化
for(int i=1; i<=n; i++)
map[node[i].point]=i;//数据离散化
memset(c,0,sizeof(c));
long long int step=0;//记录最少交换步数
for(int i=1; i<=n; i++)
{
change(map[i]);//插入映射数据
step=step+i-sum(map[i]);//每次插入都计算插入数据前还有多少比插入数据小的数据
}
printf("%lld\n",step);
}
return 0;
}
还有一道二维树状数组,晚上再写吧,没时间了。