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代码:
线段树解法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
树状数组解法(详见代码及注释):
此题用树状数组要比线段树快。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
归并排序解法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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) 编辑 收藏 举报