题目链接: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; }
#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; }