要想对任意(ai,bi)和(aj和bj),当ai<aj时,都有bi<=bj;当ai>=aj时,bi>=bj,当对a进行升序排序后(b同时发生改变,从而不改变值,最后有a1<=a2<=…<=an),必须满足b1<=b2<=…<=bn。
否则,必存在(ai,bi)和(aj和bj),有ai<aj且bi>bj,交换ai和aj后,(aj*bi+ ai*bj)-( ai*bi+ aj*bj)=(aj-ai)*( bi-bj)>0,数值更小,即不是所满足的序列。
唯有满足条件“对任意(ai,bi)和(aj和bj),当ai<aj时,都有bi<=bj;当ai>=aj时,bi>=bj”的序列才是值最小的序列。
最后目的是:对任意(ai,bi)和(aj和bj),当ai<aj时,都有bi<=bj;当ai>=aj时,bi>=bj,其中题目限制“同一列火柴的高度互不相同”,所以对a,b序列进行从小到大排序,数的编号具有唯一性,即最后a,b两个序列中,ai在a序列的编号等于bi在b序列的编号。
每一次操作,一列(a,b数组)中相邻的两个数进行交换,其中ai和ai+1的交换等效于bi和bi+1的交换。我们可以固定a序列不动,修改b序列,最优的操作满足操作次数最少。
b序列第i位的数要移到目标位置第vi位中,求出v序列。b序列的数的移动顺序是按照对应的v序列的数从小到大。当目标位置为第vi位对应的bi(原来)需要移动时,前面的第1~vi-1位已经排好,而其它bi(原来)前面的数为还未移动的数,即对应的v序列的值大于vi。所以bi(原来)需要向左移动的次数为满足j<i and vj>vi所有的j的个数。
即总的操作次数为v序列的逆序对的个数。有两种方法求逆序对使得时间复杂度为O(nlogn):
1.归并排序+统计
2.离散化+树状数组
Code:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <math.h> 4 #define yu 99999997 5 #define maxn 100000 6 7 struct node 8 { 9 long value,pos; 10 }a[maxn+1],b[maxn+1]; 11 long v[maxn+1],t[maxn+1],ans; 12 13 int cmp(const void *a,const void *b) 14 { 15 if ((*(struct node *)a).value<(*(struct node *)b).value) 16 return -1; 17 else 18 return 1; 19 } 20 21 void mergesort(long l,long r) 22 { 23 if (l==r) 24 return ; 25 long mid,x,y,z,i; 26 mid=(l+r)/2; 27 mergesort(l,mid); 28 mergesort(mid+1,r); 29 for (i=l;i<=r;i++) 30 t[i]=v[i]; 31 x=l; 32 y=mid+1; 33 z=l; 34 while (x<=mid && y<=r) 35 { 36 //两个数相等时让左边的数先加,因为相同的数不能凑成一对 37 if (t[x]<=t[y]) 38 { 39 v[z]=t[x]; 40 x++; 41 //v[x] > v[mid+1]~v[y-1] 42 ans=(ans+(y-mid-1))%yu; 43 } 44 else if (t[x]>t[y]) 45 { 46 v[z]=t[y]; 47 y++; 48 } 49 z++; 50 } 51 if (x<=mid) 52 { 53 //v[x] > v[mid+1]~v[r] 54 ans=(ans+(mid-x+1)*(r-mid))%yu; 55 while (z<=r) 56 { 57 v[z]=t[x]; 58 x++; 59 z++; 60 } 61 } 62 else 63 { 64 while (z<=r) 65 { 66 v[z]=t[y]; 67 y++; 68 z++; 69 } 70 } 71 } 72 73 int main() 74 { 75 long n,i; 76 scanf("%ld",&n); 77 for (i=1;i<=n;i++) 78 { 79 scanf("%ld",&a[i].value); 80 a[i].pos=i; 81 } 82 for (i=1;i<=n;i++) 83 { 84 scanf("%ld",&b[i].value); 85 b[i].pos=i; 86 } 87 qsort(a+1,n,sizeof(struct node),cmp); 88 qsort(b+1,n,sizeof(struct node),cmp); 89 for (i=1;i<=n;i++) 90 v[b[i].pos]=a[i].pos; 91 ans=0; 92 mergesort(1,n); 93 printf("%ld ",ans); 94 return 0; 95 } 96 /* 97 Input: 98 10 99 10 1 5 2 7 4 9 3 6 8 100 7 5 1 8 10 4 6 2 3 9 101 Output: 102 18 103 */
感慨一下:想当初比赛做这道题,没有想到是逆序对,最终没有做出来,留下遗憾。现在终于写了个题解,把这道题做了,算是对遗憾的一种弥补吧。
希望自己可以走得更远……