10 1 3 6 9 0 8 5 7 4 2
16
如果求出第一种情况的逆序列,其他的可以通过递推来搞出来,一开始是t[1],t[2],t[3]....t[N]
它的逆序列个数是N个,如果把t[1]放到t[N]后面,逆序列个数会减少t[1]个,相应会增加N-(t[1]+1)个
暴力法300ms:
#include<stdio.h> int a[5555]; int main() { int n,i,j,ans=999999999; while(scanf("%d",&n)!=EOF) { ans=999999999; for(i=0;i<n;i++) scanf("%d",&a[i]); int cnt=0; for(i=0;i<n;i++) for(j=i+1;j<n;j++) { if(a[i]>a[j]) cnt++; } // printf("cnt=%d\n",cnt); if(ans>cnt) ans=cnt; for(i=0;i<n;i++) { cnt=cnt-a[i]+n-1-a[i]; if(ans>cnt) ans=cnt; } printf("%d\n",ans); } return 0; }
下面说一下线段树的做法 31ms
用线段树去求输入序列的逆序数
方法:
把树的叶子节点作为每个数的对应位置
枚举到第i个数时,我们需要求出前i次插入的数中有多少个比a[i]大,
即去寻找已经插入的数中比a[i]大的数的个数 即查询叶子节点a[i]到n的数的个数
#include<stdio.h> int a[10000]; struct haha { int left; int right; int num; }node[10000*4]; void build(int left,int right,int nd) { node[nd].left=left; node[nd].right=right; node[nd].num=0; if(left==right) { return ; } int mid=(left+right)/2; build(left,mid,nd*2); build(mid+1,right,nd*2+1); } int query(int left,int right,int nd) { int mid=(node[nd].left+node[nd].right)/2; if(node[nd].left==left&&node[nd].right==right) { return node[nd].num; } if(right<=mid) { return query(left,right,nd*2); } else if(left>mid) { return query(left,right,nd*2+1); } else { return query(left,mid,nd*2)+query(mid+1,right,nd*2+1); } } void update(int pos,int nd) { if(node[nd].left==node[nd].right) {node[nd].num++;return ;} int mid=(node[nd].left+node[nd].right)/2; if(pos<=mid) update(pos,nd*2); else update(pos,nd*2+1); node[nd].num=node[nd*2].num+node[nd*2+1].num; } int main() { int n,i,j; while(scanf("%d",&n)!=EOF) { for(i=0;i<n;i++) scanf("%d",&a[i]); build(0,n-1,1); int sum=0; for(i=0;i<n;i++) { //printf("i=%d sum=%d\n",i,sum); sum+=query(a[i],n-1,1); // printf(">>>"); update(a[i],1); } // printf("%d\n",sum); int ans=99999999; if(ans>sum) ans=sum; for(i=0;i<n;i++) { sum=sum-a[i]+n-1-a[i]; if(ans>sum) ans=sum; } printf("%d\n",ans); } return 0; }
下面是归并排序方法:
套用归并排序模板
#include<stdio.h> #include<malloc.h> int ans,a[5050],b[5050]; void merge(int left,int mid,int right) { int i,j,cnt=0; int *p; p=(int *)malloc((right-left+1)*sizeof(int)); i=left; j=mid+1; while(i<=mid&&j<=right)//这时候i 和 j 指向的部分都排序完毕了 现在合并 { if(a[i]<=a[j]) { p[cnt++]=a[i]; i++; } else { p[cnt++]=a[j]; j++; ans+=mid-i+1;//第i个比j大 由于i已经从小到大排过序了 那么i+1到mid的也会比j大 } } while(i<=mid) { p[cnt++]=a[i++]; } while(j<=right) { p[cnt++]=a[j++]; } cnt=0; for(i=left;i<=right;i++) a[i]=p[cnt++]; free(p); } void merge_sort(int left,int right) { if(left<right) //长度大于1 这是个判断不是循环 { int mid; mid=(left+right)/2; merge_sort(left,mid); merge_sort(mid+1,right); merge(left,mid,right); } } int main() { int n,i,j ; while(scanf("%d",&n)!=EOF) { for(i=0;i<n;i++) {scanf("%d",&a[i]);b[i]=a[i];} ans=0; merge_sort(0,n-1); //printf("ans=%d\n",ans); int cnt=999999999; if(cnt>ans) cnt=ans; for(i=0;i<n;i++) { //printf("a[i]=%d\n",a[i]); ans=ans-b[i]+n-1-b[i]; if(cnt>ans) cnt=ans; } printf("%d\n",cnt); } return 0; }