题目链接http://poj.org/problem?id=2299
题目大意:
在这个问题中,你必须分析一个特定的排序算法。 该算法通过交换两个相邻的序列元素来处理n个不同整数的序列,直到序列按升序排序。 对于输入序列
9 1 0 5 4,
通过Ultra-QuickSort输出
0 1 4 5 9。
你的任务是确定Ultra-QuickSort需要执行多少交换操作才能排序给定的输入序列。
输入
该输入包含几个测试用例。 每个测试用例从一行包含单个整数n <500,000 - 输入序列的长度开始。 以下n行中的每一行包含第i个输入序列元素的单个整数0≤a[i]≤999,999,999。 输入由长度为n = 0的序列终止。该序列不能被处理。
输出
对于每个输入序列,您的程序将打印一行包含一个整数op,一个排序给定输入序列所需的最小交换操作数。
样例输入
5
9
1
0
5
4
3
1
2
3
0
样例输出
6
0
参考博客http://blog.csdn.net/SeasonJoe/article/details/50193789?locationNum=15&fps=1
解法一:树状数组
由于题目数据过大,显然考虑离散化
然后用树状数组维护
可以将原数组位置与排序后的位置对应
以排序后数组建树状数组,
如果第i位有一个数
void update(int i)
{
for(;i<=n;i+=lowbit(i))
{
c[i]++;
}
}
对于每个数,我们搜它前面比它小的数的个数
再用它原数组位置减去即是包含这个数的逆序对个数
具体看代码
#include<bits/stdc++.h>
#define ll long long
#define MAX 2147483647
#define fi first
#define se second
#define mid (l+r>>1)
#define fo(i,a,b) for(int i=(a);i<=(b);++i)
#define fodown(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
int n;
struct node{
int val,rank;
}a[500001];
int ch[500001];
int c[500005];
ll ans=0;
int read()
{
int x=0,tmp=1;
char ch=getchar();
while(ch<'0' || ch>'9' ) { if(ch=='-') tmp=-1;ch=getchar();}
while(ch>='0'&& ch<='9') { x = x * 10+ ch-'0';ch=getchar();}
return x*tmp;
}
bool cmp(node a,node b)
{
return a.val<b.val;
}
int lowbit(int x)
{
return x&(-x);
}
void update(int x)//在X位置上有一个数
{
for(;x<=n;x+=lowbit(x))
{
c[x]+=1;
}
}
int getsum(int x)
{
ll sum=0;
while(x>=1)
{
sum+=c[x];
x-=lowbit(x);
}
return sum;
}
int main()
{
// freopen(" .in","r",stdin);
// freopen(" .out","w",stdout);
while(scanf("%d",&n)&&n)
{
fo(i,1,n)
{
a[i].val=read();
a[i].rank=i;//离散化
}
sort(a+1,a+n+1,cmp);
fo(i,1,n)
ch[a[i].rank]=i;
memset(c,0,sizeof(c));
ans=0;
fo(i,1,n)
{
update(ch[i]);
ans+=i-getsum(ch[i]);
}
cout<<ans<<endl;
}
return 0;
}
解法二:归并排序
显然,对于本题树状数组过于高大上了,数据范围大,难求前缀和,一定要离散化,复杂度应该比归并略高
如何想到归并?
题目要你想一个算法,求出将原数组排序后交换的次数
首先想到冒泡排序,但代价太高
快排又不行
O(nlog(n))算法只有归并可行(归并我只会求逆序对)
具体看代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
#define MAX 2147483647
#define fi first
#define se second
#define fo(i,a,b) for(int i=(a);i<=(b);++i)
#define fodown(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
int n,a[500005],b[5000005];
ll ans;
int read()
{
int x=0,tmp=1;
char ch=getchar();
while(ch<'0' || ch>'9' ) { if(ch=='-') tmp=-1;ch=getchar();}
while(ch>='0'&& ch<='9') { x = x * 10+ ch-'0';ch=getchar();}
return x*tmp;
}
void merge(int l,int m,int r)
{
int i=l,j=m+1,tot=l-1;
while(i<=m&&j<=r)
{
if(a[i]<=a[j]) b[++tot]=a[i],i++;
else
{
b[++tot]=a[j];
ans+=m-i+1;/*我们能保证(l,m)这个区间是升序的,这是显然的,因为先排序这个区间,再排序整个区间,如果a[j]<a[i],那么a[j]与a[i]是逆序对,和a[i]后面的数(a[i~m])也是逆序对,因此个数加上m-i+1*/
j++;
}
}
while(i<=m)
{
b[++tot]=a[i];
i++;
}
while(j<=r)
{
b[++tot]=a[j++];
}
fo(t,l,r)
a[t]=b[t];
}
void qsort(int l,int r)
{
if(l==r) return ;
int mid=l+r>>1;
qsort(l,mid);//分块
qsort(mid+1,r);
merge(l,mid,r);
}
int main()
{
// freopen(" .in","r",stdin);
// freopen(" .out","w",stdout);
while(scanf("%d",&n)!=EOF&&n)
{
fo(i,1,n) a[i]=read();
ans=0;
qsort(1,n);
cout<<ans<<endl;
}
return 0;
}