Ultra-QuickSort
| Time Limit: 7000MS | Memory Limit: 65536K | |
| Total Submissions: 69564 | Accepted: 26072 |
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
题意:一组无序数字,每每相邻的两个数互换,换多少次可以使其升序。其实本质是求逆序对数,逆序对就是位置在前但值小于位置在后的值,例如5,3,两两一组就是逆序对。为什么就是逆序对数?因为逆序对的两个数是一定有一次移动的,每次移动都是在逆转逆序对,不可能移动排好的或者相等的,那没有意义,把所有的逆序反过来就是结果了。反言之,求逆序对数就是互换的次数。
in.txt
5
9 1 0 5 4
3
1 2 3
0
归并排序法
思路:归并就是递归合并,把整个部分二分,然后每部分排好序,在合并插入。
首先理解两个排好序的数组,怎么合并?
举例:1,3,5,7 2,4,8,9
重头开始判断,哪个小放哪个,最后有两种情况,第一个数组先拍完或者第二个数组先排完,把剩下的塞后面就是了。
然后是怎么得到两个有序的数组呢,这就要递归和二分了,这个不好描述,代码更清晰。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
typedef long long ll;
const int N=5e5+10;
int a[N],arr[N];
ll Sort(int low,int mid,int high)
{
int i=low,j=mid+1,k=low;
ll ans=0;
while(i<=mid&&j<=high){
if(a[i]<=a[j])
arr[k++]=a[i++];
else{
arr[k++]=a[j++];
ans+=j-k;
}
}
while(i<=mid) arr[k++]=a[i++];
while(j<=high) arr[k++]=a[j++];
for(int i=low;i<=high;i++)
a[i]=arr[i];
return ans;
}
ll MergeSort(int low,int high)
{
if(low>=high) return 0;
ll sum=0;
int mid=(low+high)/2;
sum+=MergeSort(low,mid);
sum+=MergeSort(mid+1,high);
sum+=Sort(low,mid,high);
return sum;
}
int main()
{
//freopen("in.txt","r",stdin);
int n;
while(scanf("%d",&n)!=EOF&&n){
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
printf("%I64d\n",MergeSort(0,n-1));
/*
for(int i=0;i<n;i++)
printf("a[%d]=%d\n",i,a[i]);
*/
}
return 0;
}
这题要注意一个细节,本题sum最大的情况就是500,000的递减序列,这需要500000*500000/2,是比较大的,用int会出错,要用long long或者__int64。还有就是上述写法是求的移动次数。
树状数组
树状数组不怎么好理解,那我们就不理解它的难点,我们只要知道最基本的,树状数组能得到什么?
void build(int x)
{
for(int i=x;i<=n;i+=lowbit(i))
tree[i]++;
}
int get_sum(int x)
{
int res=0;
for(int i=x;i>=1;i-=lowbit(i))
res+=tree[i];
return res;
}
撇开细节,这是最基本的树状数组代码,我的理解是把数据大小以二进制的思想排列,我们平时是以十进制的思想排列数组,如果不理解没关系,我也不是很懂,上面这样的代码是做了件这样的事:build把一个数据按顺序插入树中,get_sum判断一个数据在树中有多少个数据不大于它。这可以用暴力去理解,但无法用暴力的方法去做,会超时。
然后思考这个东西有什么用?要求的是交换次数,也就是说我们只要知道顺序,数据的值无所谓,容易得到排好序的结果:
9 1 0 5 4 ——> 0 1 4 5 9
我们保留原位置的标号,例如(9 1 0 5 4)的标号(1 2 3 4 5),排序后就是(5 2 1 4 3),再用上述的树状数组就会得到(1 1 1 3 3),而如果一开始的数据是排好的,如果输入的是(0 1 4 5 9),那么排序后标号(1 2 3 4 5),树状结果(1 2 3 4 5),差异就在这,很明显(1 2 3 4 5)就是排列好的结果,这与(1 1 1 3 3)的差值就是需要移动的次数。这还要记住一点,移动是不会改变其他逆序对的。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define lowbit(x) (x&(-x))
using namespace std;
const int N=5e5+10;
int num[N],tree[N];
int n;
ll sum=0;
struct node
{
int value,sign;
}arr[N];
void build(int x)
{
for(int i=x;i<=n;i+=lowbit(i))
tree[i]++;
}
int get_sum(int x)
{
int res=0;
for(int i=x;i>=1;i-=lowbit(i))
res+=tree[i];
return res;
}
bool cmp(node n1,node n2)
{
return n1.value<n2.value;
}
int main()
{
//freopen("in.txt","r",stdin);
while(scanf("%d",&n)==1&&n){
sum=0;
memset(tree,0,sizeof(tree));
for(int i=1;i<=n;i++){
scanf("%d",&arr[i].value);
arr[i].sign=i;
}
sort(arr+1,arr+n+1,cmp);
for(int i=1;i<=n;i++)
num[arr[i].sign]=i;
for(int i=1;i<=n;i++){
build(num[i]);
//printf("num=%d get_sum=%d\n",num[i],get_sum(num[i]));
sum+=i-get_sum(num[i]);
}
printf("%I64d\n",sum);
}
return 0;
}
博客详细介绍了 Ultra-QuickSort 算法,该算法通过相邻元素交换实现序列排序,并给出了求解逆序对数的方法。同时,讨论了归并排序法的递归合并思想,并强调在处理大规模数据时需要注意数据类型溢出问题。此外,文章还简单介绍了树状数组的概念及其在计算交换次数中的应用,通过比较排序前后树状数组的差异来确定所需交换操作的次数。
418

被折叠的 条评论
为什么被折叠?



