题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4911
http://vjudge.net/contest/view.action?cid=52719#problem/A
1.题意:
一个包含n个数的数列,你可以操作不超过k次,每次可以交换相邻的两个数。问你最后逆序对数目的最小值。
2.题解:
(1)每次交换操作,只能减少一个逆序对,例如 (a>b)(b>c)(c>d)有a,b,c,d这个数列,通过交换(b,c)得到a,c,b,d,那么只是(b,c)这对逆序对消失了,其他的逆序关系不受影响。
(2)如果原数列含有sum个逆序对,那么你通过交换可以得到Max(sum-k,0)个逆序对
(3)统计逆序对的朴素算法是n^2的复杂度,归并法用nlogn的复杂度就可以解决。
(4)网上对于归并法的介绍很多,但是我看到的博客只有一篇看懂了,大多数都没有仔细说,这里我就介绍一遍归并求逆序对的方法。
假如我们要将两个有序(从小到大)数列Array[l,m],Array[m+1,r]合并成一个的有序数列,我们可以这样做。用p指向第一个数组的首位,用q指向第二个数组的首位,用cur指向临时数组的首位,每次把小的那一个插到临时数组的末尾,同时移动指针,这样最后临时数组就是一个有序的了。操作过程中,当A[p]<=A[q]时,t[cur++]=A[p++](A[p]更小,就将A[p]插到t的末尾),当A[p]>A[q]时,由于A是从小到大排列的,A[p,m]>A[q],所以在l~m之间有m-p+1个数可以和A[q]构成逆序对(对于任意位置上的那个数,每次都只会扫到位置在他前面且值比他大的数,而且每次一旦扫到,那个位置在他前面且值比他大的数在临时数组里就会放到他的后面,保证不会重复计算到),接着插入临时数组t[cur++]=A[q++]。
(5)本题还有树状数组的解法,比较容易理解,所以再介绍一遍树状数组的。
首先10^9的范围太大了,所以将数值离散化成10^5的,方法就是先排序,再去除重复的值。原数组从后到前扫描一遍复杂度为n,对于每个数,用树状数组看在他后面有多少个比他小的数的复杂度时logn,这样总的复杂度是nlogn。
归并code:
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN=111111;
int a[MAXN],t[MAXN];
typedef long long LL;
LL ans;
void merge(int l,int mid,int r){
int p=l,q=mid+1,cur=l;
for(int i=l;i<=r;i++)
t[i]=a[i];
while(p<=mid&&q<=r){
if(t[p]<=t[q]){
a[cur++]=t[p++];
}else{
a[cur++]=t[q++];
ans+=mid-p+1;
}
}
while(p<=mid)
a[cur++]=t[p++];
while(q<=r)
a[cur++]=t[q++];
}
void mergesort(int l,int r){
if(l>=r)return;
int m=(l+r)>>1;
mergesort(l,m);
mergesort(m+1,r);
merge(l,m,r);
}
int main(){
int n,k;
while(scanf("%d%d",&n,&k)!=EOF){
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
ans=0;
mergesort(1,n);
printf("%I64d\n",max(0LL,ans-k));
}
return 0;
}
树状数组code:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN=111111;
int a[MAXN],t[MAXN],b[MAXN],n,k,m;
int query(int x){
int s=0;
while(x){
s+=a[x];
x-=x&(-x);
}
return s;
}
void add(int x){
while(x<=n){
a[x]++;
x+=x&(-x);
}
}
int bs(int v){
int l=1,r=m,mid;
while(l<=r){
mid=(l+r)>>1;
if(v==b[mid]) break;
if(b[mid]>v) r=mid-1;
else l=mid+1;
}
return mid;
}
int main(){
while(scanf("%d%d",&n,&k)!=EOF){
for(int i=1;i<=n;i++){
scanf("%d",&t[i]);
b[i]=t[i];
a[i]=0;
}
sort(b+1,b+1+n);
m=unique(b+1,b+1+n)-(b+1);
LL ans=0;
for(int i=n,p;i;i--){
p=bs(t[i]);
add(p);
ans+=query(p-1);
}
printf("%I64d\n",max(0LL,ans-k));
}
return 0;
}