归并排序,树状数组 两种步骤求逆序对
归并排序,树状数组 两种方法求逆序对
我们知道,求逆序对最典型的方法就是树状数组,但是还有一种方法就是Merge_sort(),即归并排序。
实际上归并排序的交换次数就是这个数组的逆序对个数,为什么呢?
我们可以这样考虑:
归并排序是将数列a[l,h]分成两半a[l,mid]和a[mid+1,h]分别进行归并排序,然后再将这两半合并起来。
在合并的过程中(设l<=i<=mid,mid+1<=j<=h),当a[i]<=a[j]时,并不产生逆序数;当a[i]>a[j]时,在
前半部分中比a[i]大的数都比a[j]大,将a[j]放在a[i]前面的话,逆序数要加上mid+1-i。因此,可以在归并
排序中的合并过程中计算逆序数.
题目:http://poj.org/problem?id=1804
题意:给定一个序列a[],每次只允许交换相邻两个数,最少要交换多少次才能把它变成非递降序列.
#include <iostream> #include <string.h> #include <stdio.h> using namespace std; const int N = 1005; int a[N],tmp[N]; int ans; void Merge(int l,int m,int r) { int i = l; int j = m + 1; int k = l; while(i <= m && j <= r) { if(a[i] > a[j]) { tmp[k++] = a[j++]; ans += m - i + 1; } else { tmp[k++] = a[i++]; } } while(i <= m) tmp[k++] = a[i++]; while(j <= r) tmp[k++] = a[j++]; for(int i=l;i<=r;i++) a[i] = tmp[i]; } void Merge_sort(int l,int r) { if(l < r) { int m = (l + r) >> 1; Merge_sort(l,m); Merge_sort(m+1,r); Merge(l,m,r); } } int main() { int n,T,tt=1; scanf("%d",&T); while(T--) { scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d",&a[i]); ans = 0; Merge_sort(0,n-1); printf("Scenario #%d:\n%d\n\n",tt++,ans); } return 0; }
树状数组求逆序的思路:
可以把数一个个插入到树状数组中, 每插入一个数, 统计比他小的数的个数,对应的逆序为 i- getsum( data[i] ),其中 i 为当前已经插入的数的个数, getsum( data[i] )为比 data[i] 小的数的个数,i- getsum( data[i] ) 即比 data[i] 大的个数, 即逆序的个数。最后需要把所有逆序数求和,就是在插入的过程中边插入边求和。
下面是代码(不是poj那题的):
#include <iostream> using namespace std; #define N 10 struct Node{ int data; int pos; }; Node d[N+1]; int inverse[N+1]; int count[N]; int cmp(const void*a,const void*b) { Node *pa=(Node*)a; Node *pb=(Node*)b; return pa->data-pb->data; } int lowbit(int t){ return t & (t^(t-1)); } void modify(int pos,int num) { while (pos<=N) { inverse[pos]+=num; pos+=lowbit(pos); } } int sum(int end) { int sum=0; while (end>0) { sum+=inverse[end]; end-=lowbit(end); } return sum; } int main() { memset(inverse,0,sizeof(inverse)); //初始化 memset(count,0,sizeof(count)); char* a="9854623870"; //长度N for(int i=0;i<strlen(a);i++) { d[i+1].data =a[i]-'0'; d[i+1].pos=i+1; } qsort(d+1,N,sizeof(Node),cmp); int id=1; count[d[1].pos]=1; for(int i=2;i<=N;i++) { if(d[i].data==d[i-1].data) count[d[i].pos]=id; else count[d[i].pos]=++id; } int num=0; for(int i=1;i<=N;i++) { modify(count[i],1); num+=i-sum(count[i]); } cout<<num<<endl; return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。