Minimum Inversion Number HDU 1394

 

题意:给出n个数的一个排列,要求求出这列数按如下变换得到的n个排列中最小的逆序数。(逆序数:a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i < j and ai > aj)

例如原数列为:a1,a2,a3......an。

第一个数列:原数列:a1,a2,a3......an。

第二个数列:将原数列的第一个元素移到末尾得到的数列:a2,a3......an,a1。

第三个数列:将上述得到的数列的第一个元素移到末尾得到的数列:a3,a4......an,a1,a2。

以此类推......

 

题解:线段树(树状数组和归并排序亦可解之,下附代码)。

这里详细讲解如何利用线段树求解逆序数的:

 先建一个空树; 逐个插入值(即输入的一个值); 在每插入一个值后就更新包含该区间的所有的数的个数(加一)。 为什么可以这样呢,下面引用一个人的说明:

 

线段树求逆序数  

3 2 5 4 6 1

插入 3 时  询问 3-6 之间元素的个数  v1=0

插入 2 时  询问 2-6 之间元素的个数  v2=1

......

......                                                         v6=6

累加v1……v6  =sum。

再详细点,举个例子,插入2的时候,你要的找的数肯定是比2大的数的个数(当然是已经插进来了的,就是序列里排在2前面的),这时候发现只有3比2先被插入到树中(且只有3而已),那么2由2产生的逆序数就是1了,累加到sum中;之后更新,update(2,1),把包含2的所有区间的数的个数都+1,便于之后的1,0,去查找。

 

AC代码:

线段树解法:

 

View Code
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 using namespace std;
 5 const int maxn=5005;
 6 int sum[maxn<<2],num[maxn];
 7 int Query(int l,int r,int left,int right,int pos){
 8     if(l<=left&&right<=r)return sum[pos];
 9     int mid=(left+right)>>1;
10     if(r<=mid)return Query(l,r,left,mid,pos*2);
11     if(l>mid)return Query(l,r,mid+1,right,pos*2+1);
12     int lsum=Query(l,r,left,mid,pos*2);
13     int rsum=Query(l,r,mid+1,right,pos*2+1);
14     return lsum+rsum;
15 }
16 void Updata(int p,int l,int r,int pos){
17     if(l==r){
18         sum[pos]++;
19         return;
20     }
21     int mid=(l+r)>>1;
22     if(p<=mid)Updata(p,l,mid,pos*2);
23     else      Updata(p,mid+1,r,pos*2+1);
24     sum[pos]=sum[pos*2]+sum[pos*2+1];
25 }
26 int main()
27 {
28     int n;
29     while(~scanf("%d",&n)){
30         memset(sum,0,sizeof(sum));
31         int sum=0;
32         for(int i=0;i<n;i++){
33             scanf("%d",&num[i]);
34             sum+=Query(num[i],n-1,0,n-1,1);
35             Updata(num[i],0,n-1,1);
36         }
37         int ans=sum;
38         for(int i=0;i<n-1;i++){
39             sum+=n-num[i]-num[i]-1;
40             ans=ans<sum?ans:sum;
41         }
42         printf("%d\n",ans);
43     }
44     return 0;
45 }

 

 

 

树状数组解法(详见代码及注释):

此题用树状数组要比线段树快。

 

View Code
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 using namespace std;
 5 const int maxn=5005;
 6 int num[maxn],c[maxn];
 7 int n;
 8 int lowbit(int x){
 9     return x&(-x);
10 }
11 int SuM(int p){//统计1--p的和,即1--p中有多少个数已经出现了,也就是计算比p小的数的个数
12     int sum=0;
13     while(p>0){
14         sum+=c[p];
15         p-=lowbit(p);
16     }
17     return sum;
18 }
19 void Updata(int p,int val){
20     while(p<=n){
21         c[p]+=val;
22         p+=lowbit(p);
23     }
24 }
25 int main()
26 {
27     while(~scanf("%d",&n)){
28         int sum=0;
29         memset(c,0,sizeof(c));
30         for(int i=0;i<n;i++){
31             scanf("%d",&num[i]);
32             sum+=SuM(n)-SuM(++num[i]);//这里num[i]全部都要加一!否则遇到0时在Updata()时会死循环。
33             //每输入一个数首先逆序数
34             Updata(num[i],1);
35             //更新c[]数组,使所有相应的c[]的加一,表示num[i]在第i次已经出现
36         }
37         int ans=sum;
38         for(int i=0;i<n-1;i++){
39             sum+=n-num[i]-num[i]+1;
40             ans=ans<sum?ans:sum;
41         }
42         printf("%d\n",ans);
43     }
44     return 0;
45 }

 

 归并排序解法:

 

View Code
 1 #include<cstdio>
 2 #include<iostream>
 3 using namespace std;
 4 const int maxn=5005;
 5 int num1[maxn],num2[maxn],temp[maxn];
 6 int sum;
 7 void Merge(int l,int mid,int r){
 8     int p=0;
 9     int i=l,j=mid+1;
10     while(i<=mid&&j<=r){
11         if(num1[i]>num1[j]){
12             sum+=mid-i+1;
13             temp[p++]=num1[j++];
14         }
15         else temp[p++]=num1[i++];
16     }
17     while(i<=mid)temp[p++]=num1[i++];
18     while(j<=r)temp[p++]=num1[j++];
19     for(i=0;i<p;i++)
20         num1[l+i]=temp[i];
21 }
22 void MergeSort(int l,int r){
23     if(l<r){
24         int mid=(l+r)>>1;
25         MergeSort(l,mid);
26         MergeSort(mid+1,r);
27         Merge(l,mid,r);
28     }
29 }
30 int main()
31 {
32     int n;
33     while(~scanf("%d",&n)){
34         for(int i=0;i<n;i++){
35             scanf("%d",&num1[i]);
36             num2[i]=num1[i];
37         }
38         sum=0;
39         MergeSort(0,n-1);
40         int ans=sum;
41         for(int i=0;i<n-1;i++){
42             sum+=n-num2[i]-num2[i]-1;
43             ans=ans<sum?ans:sum;
44         }
45         printf("%d\n",ans);
46     }
47     return 0;
48 }

 

 

 

 

 

      

posted on 2012-10-18 23:28  Acmer_Roney  阅读(176)  评论(0编辑  收藏  举报

导航