一开始以为是POJ 2299 的翻版,归并排序求逆序数
于是写出了以下的超时代码:
#include <stdio.h> #define inf 1000000000 int n; int a[10010]; int minnixu; int nixu; int num[10010]; int bak[10010]; void mergesort (int L, int R) { if (L >= R) return; int mid = (L + R) / 2; mergesort (L, mid); mergesort (mid + 1, R); for (int k = L; k <= R; ++k) bak[k] = num[k]; int i = L, j = mid + 1; for (int k = L; k <= R; ++k) if (i <= mid && (j > R || bak[i] < bak[j])) { num[k] = bak[i]; i ++; } else { num[k] = bak[j]; nixu+=mid-i+1; j ++; } } int main() { while (scanf("%d",&n)!=EOF) { minnixu=inf; for (int i=0; i<n; i++) { scanf("%d",a+i); a[n+i]=a[i]; } for (int i=0; i<n; i++) { nixu=0; for (int j=0; j< 2*n; j++) num[j]=a[j]; mergesort(0+i,i+n-1); if(nixu<minnixu) minnixu=nixu; } printf("%d\n",minnixu); } }
后来一想。。。这么做的复杂度是 n^2 * log(n) ,的确让人无法接受,于是着手分析题意:
题目中的数列是 0到n-1的全排列
也就是说,每当我们做一次移位操作(将首元素 a 移到尾部)移位后数列的逆序数为移位前的逆序数 -a + (n-1-a) (-a是因为a之后有a个比a小的数,把a移到队尾,这些逆序将不存在;同理,+(n-1-a)是因为a之后有n-1-a个比a大的数,把a移到队尾,会形成相应的逆序)
于是利用此公式写出AC代码:
#include <stdio.h> #define inf 1000000000 int n; int a[10010]; int rs[10010]; //right small int count; int mincount; int main() { while (scanf("%d",&n)!=EOF) { count=0; mincount=inf; for (int i=0; i<n; i++) { scanf("%d",a+i); rs[i]=0; } for(int i=0; i<n; i++) { for(int j=i+1;j<n; j++) { if(a[j]<a[i]) rs[i]++; } } for(int i=0;i<n;i++) { count+=rs[i]; } if(count < mincount) mincount=count; for(int i=0;i<n;i++) { count+=n-1-a[i]; count-=a[i]; if(count < mincount) mincount=count; } printf("%d\n",mincount); } }