求逆序数的方法--线段树法&归并排序法
逆序数的概念:对于n个不同的元素,先规定各元素之间有一个标准次序(例如n个 不同的自然数,可规定从小到大为标准次序),于是在这n个元素的任一排列中,当某两个元素的先后次序与标准次序不同时,就说有1个逆序。一个排列中所有逆序总数叫做这个排列的逆序数。
解决思路:
HDU-1394
1、线段树:通过保存区间内数的出现次数,每次插入一个数前把比它小(大)的区间内的总数累加。
1 //求逆序数 线段树法 2 //原理,因为输入是依次输入的,所以每次输入一个数,只要查找比这个数小数的个数并累加就行了。 3 //顺序查找的复杂度是O(n),很容易想到用线段树保存每个数的出现次数,每次输入在比他小(大)的区间中查找总数就行了 4 #include <iostream> 5 #define N 5010 6 using namespace std; 7 int tree[N<<2];//线段树保存了每个区间内数字出现总数 8 void CreatTree(int node,int l,int r) 9 { 10 if(l==r) 11 { 12 tree[node]=0; 13 return ; 14 } 15 int mid=(l+r)>>1; 16 CreatTree(node<<1, l, mid); 17 CreatTree(node<<1|1, mid+1, r); 18 tree[node]=tree[node<<1]+tree[node<<1|1]; 19 } 20 void Insert(int node,int num,int l,int r) 21 { 22 if(l==num&&r==l) 23 { 24 tree[node]++; 25 return ; 26 } 27 int mid=(l+r)>>1; 28 if(num<=mid) 29 Insert(node<<1, num, l, mid); 30 else 31 Insert(node<<1|1, num, mid+1, r); 32 tree[node]=tree[node<<1]+tree[node<<1|1]; 33 } 34 int Query(int node,int ql,int qr,int l,int r) 35 { 36 if(qr<ql) 37 return 0; 38 if(ql<=l&&r<=qr) 39 return tree[node]; 40 int mid=(l+r)>>1; 41 int rec=0; 42 if(ql<=mid) 43 rec+=Query(node<<1, ql, qr, l, mid); 44 if(qr>mid) 45 rec+=Query(node<<1|1, ql, qr, mid+1, r); 46 return rec; 47 } 48 int main(int argc, const char * argv[]) { 49 int n; 50 while(cin>>n) 51 { 52 int ans=0,temp; 53 int num[N]; 54 CreatTree(1, 0, n-1); 55 for(int i=0;i<n;i++) 56 { 57 cin>>temp; 58 num[i]=temp; 59 Insert(1, temp, 0, n-1); 60 ans+=Query(1, temp+1,n-1, 0, n-1); 61 } 62 int sum=ans; 63 for(int i=0;i<n;i++) 64 { 65 ans=ans-num[i]+n-num[i]-1; 66 if(ans<sum) 67 sum=ans; 68 } 69 cout<<sum<<endl; 70 } 71 return 0; 72 }
2、归并排序:归并排序过程中,如果左序列的第j个值大于有序列第k个值,那么j~lenl的值都大于k,所以每次ans+=
id-(left+j)+1。完成整个归并排序,就可以统计所有逆序数对的数量。
1 //求逆序数 归并排序法 2 //在归并排序过程中,每次对左右两个有序数列排序时,如果左边序列的第j个值大于右边序列k个值 3 //那么说明左边序列的j~lenl个值都大于第k个值,所以把其加到ans中 4 #include <iostream> 5 #define N 5010 6 using namespace std; 7 bool cmp(int a,int b) 8 { 9 return a<=b; 10 } 11 int ans; 12 void MergeArr(int num[], int left, int mid, int right) 13 { 14 int AL[N], AR[N],lenl=mid-left+1,lenr=right-mid; 15 for (int i = 0;i < lenl;i++) 16 AL[i] = num[left + i]; 17 for (int i = 0;i < lenr;i++) 18 AR[i] = num[mid + 1 + i]; 19 int j = 0, k = 0,pos=left; 20 while (j < lenl&&k < lenr) 21 { 22 if (cmp(AL[j], AR[k])) 23 num[pos++] = AL[j++]; 24 else 25 num[pos++] = AR[k++],ans+=mid-(left+j)+1;//关键步骤 26 } 27 while (j < lenl) 28 num[pos++] = AL[j++]; 29 while (k < lenr) 30 num[pos++] = AR[k++]; 31 } 32 void MergeSort(int num[], int left, int right) 33 { 34 if (left < right) 35 { 36 int mid = (right + left) / 2; 37 MergeSort(num, left, mid); 38 MergeSort(num, mid+1, right); 39 MergeArr(num, left, mid, right); 40 } 41 } 42 int main() { 43 int n; 44 while(cin>>n) 45 { 46 ans=0; 47 int num[N],temp[N]; 48 for(int i=0;i<n;i++) 49 cin>>num[i],temp[i]=num[i]; 50 MergeSort(temp, 0, n-1); 51 int sum=ans; 52 for(int i=0;i<n;i++) 53 { 54 ans=ans-num[i]+n-num[i]-1; 55 if(ans<sum) 56 sum=ans; 57 } 58 cout<<sum<<endl; 59 } 60 return 0; 61 }