首页 > 代码库 > 归并排序,树状数组 两种方法求逆序对
归并排序,树状数组 两种方法求逆序对
我们知道,求逆序对最典型的方法就是树状数组,可是另一种方法就是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; }
树状数组求逆序:
http://poj.org/problem?id=2299
思路: 离散化+树状数组
分析:
1 题目的意思就是要求逆序数对
2 题目的输入个数有500000的规模可是每一个数的最大值为999999999。因此我们须要离散化这些数据
3 对于数据9 1 0 5 4我们离散化成5 2 1 4 3
那么对于输入一个树a[i]我们去求一下它的离散化后的id,然后去求前面比这个id大的个数
4 因为getSum(x)函数的求和是求[1,x]而不是[x。MAXN),所以我们能够换成求小于等于id的个数即getSum(id),然后i-1-getSum(id)就是比id大的个数,最后在更新一下treeNum[id]
代码:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<vector> using namespace std; const int MAXN = 500010; int n; int tmpNum[MAXN] , num[MAXN]; int treeNum[MAXN]; int lowbit(int x){ return x&(-x); } int getSum(int x){ int sum = 0; while(x){ sum += treeNum[x]; x -= lowbit(x); } return sum; } void add(int x , int val){ while(x < MAXN){ treeNum[x] += val; x += lowbit(x); } } long long solve(){ long long ans = 0; memcpy(tmpNum , num , sizeof(num)); memset(treeNum , 0 , sizeof(treeNum)); sort(num+1 , num+1+n); int len = unique(num+1 , num+1+n)-(num+1); for(int i = 1 ; i <= n ; i++){ int id = lower_bound(num+1 , num+1+n,tmpNum[i])-num; ans += i-getSum(id)-1; add(id , 1); } return ans; } int main(){ while(scanf("%d" , &n) && n){ for(int i = 1 ; i <= n ; i++) scanf("%d" , &num[i]); printf("%lld\n" , solve()); } return 0; }
归并排序,树状数组 两种方法求逆序对