题目链接:
http://codeup.cn/problem.php?cid=100000636&pid=0
题目:
问题 A: 最少的交换
时间限制: 1 Sec 内存限制: 32 MB
题目描述
现在给你一个由n个互不相同的整数组成的序列,现在要求你任意交换相邻的两个数字,使序列成为升序序列,请问最少的交换次数是多少?
输入
输入包含多组测试数据。每组输入第一行是一个正整数n(n<500000),表示序列的长度,当n=0时。
接下来的n行,每行一个整数a[i](0<=a[i]<=999999999),表示序列中第i个元素。
输出
对于每组输入,输出使得所给序列升序的最少交换次数。
样例输入
5
9
1
0
5
4
3
1
2
3
0
样例输出
6
0
思路:
每次只能交换相邻的两个数,实则最少的交换次数就是逆序对次数。逆序对满足: i < j & & a [ i ] > a [ j ] i<j\&\&a[i]>a[j] i<j&&a[i]>a[j]。
这里采用归并排序和树状数组两种方式来求解。
对于归并排序,因为涉及到a[i]和a[j]两两比较,每次二分完成后。对于局部已经排序好的a[]数组,如果i
对于树状数组,首先采用hash的思想,对于每个出现的数每出现一次就加一,那怎么求逆序对呢?利用getsum(x)求出x前面(包括x)已经出现的值的次数,再利用已经插入的个数减去getsum(x)即可。由于a[i]的最大值已经超过了int,这里首先离散化,即x表示的并不是x本身,而是x出现的索引。
归并排序求逆序对 code:
#include
#include
#include
#define ll long long
using namespace std;
ll a[500005],d[500005];
ll sum=0;
void msort(int l,int r){
if(l>=r) return;
int mid=(l+r)>>1;
msort(l,mid);msort(mid+1,r);
int i=l,j=mid+1,t=l;
while(i<=mid&&j<=r){
if(a[i]<=a[j]){
d[t++]=a[i++];
}else{
d[t++]=a[j++];
sum+=mid-i+1;//统计逆序对数目,注意sum应为long long类型
}
}
while(i<=mid) d[t++]=a[i++];
while(j<=r) d[t++]=a[j++];
for(int k=l;k<=r;k++) a[k]=d[k];
}
int main(int argc, char** argv) {
int n;
while(scanf("%d",&n)!=EOF&&n){
memset(d,0,sizeof(d));
sum=0;
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
msort(1,n);
printf("%lld\n",sum);
}
return 0;
}
树状数组求逆序对 code:
#include
#include
#include
#include
#define ll long long
#define lowbit(x) (x&(-x))
using namespace std;
struct node{
ll v;
int ind;
bool operator <(const node &b) const{
return v<b.v;//
}
}num[500005];
ll C[500005],A[500005];
void update(int x,int v){
for(int i=x;i<500005;i+=lowbit(i)){
C[i]+=v;
}
}
ll getsum(int x){
ll sum=0;
for(int i=x;i>0;i-=lowbit(i)){
sum+=C[i];
}
return sum;
}
int main(int argc, char** argv) {
int n;
while(scanf("%d",&n)!=EOF&&n){
memset(C,0,sizeof(C));
memset(num,0,sizeof(num));
memset(A,0,sizeof(A));
for(int i=1;i<=n;i++){
scanf("%lld",&num[i].v);
num[i].ind=i;
}
sort(num+1,num+n+1);
for(int i=1;i<=n;i++){//离散化
if(i==1||num[i].v!=num[i-1].v) A[num[i].ind]=i;
else if(num[i].v==num[i-1].v) A[num[i].ind]=A[num[i-1].ind];
}
ll sum=0;
for(int i=1;i<=n;i++){
update(A[i],1);//A[i] int范围内
sum+=i-getsum(A[i]);
}
printf("%lld\n",sum);
}
return 0;
}