poj.2299

来源:互联网 发布:linux 僵尸进程 编辑:程序博客网 时间:2024/05/22 10:44

这道题目线段树解决,不过也可以用逆序数解决,这里直接给出一个结论,一个偏续序列通过两两交换最终达到有序的最小交换次数=该序列的逆序数。那么什么是逆序数呢?

这里以题目中给出的例子做简单的介绍:

9 1 0 5 4,这里要从小到大排序,那么4的逆序数为2,因为5前面有9和5比它大,依此类推,5,0,1,9的逆序数分别为1,2,1,0,这样逆序数总共为6,所以我们的任务就是求逆序数了。实际上也可以直接用冒泡排序和插入排序做,因为他们的原理就是基于交换相邻元素的值,但是他们的时间复杂度为O(N^2),如果用他们,铁定超时。用改善版的冒泡也不行,时间复杂度依旧很高,用希尔排序,虽然时间复杂度可以满足题目要求,但其原理和插入并非完全一样,无法找到对应关系。所以要用到时间复杂度比较小的排序算法,首先是快排,但是其原理并非是交换相邻的两个元素的值,无法找到与逆序数对应的关系,故舍弃,堆排序也一样,最后想到的是归并排序,他们的时间复杂度都为O(nlogn),归并排序的原理也并非是交换相邻元素的值,其思想为先分后和,我们可以利用和的过程将逆序数求出,具体就不详述了,看代码,你就会明白。下面是代码:

#include <stdio.h>#include <stdlib.h>#include <iostream>#define Max 500010using namespace std;__int64 record[Max];__int64 ans;int n;void Merge(__int64 *p,int start,int middle,int end){ __int64 *temp=new __int64[end-start+1]; int i=start,j=middle+1,k=0; while(i<=middle && j<=end){  if(p[i]<=p[j]){   temp[k]=p[i];   k++,i++;  }  else{   temp[k]=p[j];   ans+=middle-i+1;   k++,j++;  } } while(i<=middle)  temp[k++]=p[i++]; while(j<=end)  temp[k++]=p[j++]; for(i=start;i<=end;i++)  p[i]=temp[i-start]; delete []temp;}void Merge_sort(__int64 *p,int start,int end){ if(start<end){  int middle=(start+end)>>1;  Merge_sort(p,start,middle);  Merge_sort(p,middle+1,end);  Merge(p,start,middle,end); }}int main(){ while(scanf("%d",&n),n){  for(int i=0;i<n;i++)   scanf("%lld",&record[i]);  ans=0;  Merge_sort(record,0,n-1);  printf("%lld\n",ans); } return 0;}


 

0 0
原创粉丝点击