求逆序数的方法--线段树法&归并排序法

逆序数的概念:对于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 }

 

posted @ 2016-08-15 16:04  Luke_Ye  阅读(444)  评论(0编辑  收藏  举报