LightHouse/归并排序

灯塔(LightHouse)

题目见https://dsa.cs.tsinghua.edu.cn/oj/problem.shtml?id=1144
最近复习DSA,便在看邓老师的mooc,顺便做做配套的题目,挺有意思的。

一、题目分析

简述思路:两次排序,第一次是对结构体的x坐标进行排序,第二次是计数y坐标中的逆序对/顺序对的数目。
第一次排序可以采用快速排序/归并排序等
第二次就是归并排序计算逆序对数目

注意点如下:

  • 数据范围大小,合适的地方需要使用long long,注意n*n结果范围可能超过int,此时也需要long long n;
  • new和delete需要配合使用。int* B=new int[lenB];...delete[] B;否则会造成内存泄漏,oj显示MLE
  • 归并排序的归并部分写法,最优写法如下
for(int i=0,j=0,k=0;i<lenB;){
  if(j>=lenC||B[i]<C[j]){
    A[k++]=B[i++];
    if(j<lenC)count+=(lenC-j);//顺序对在这里出现
  }
  if(j< lenC&&C[j]<=B[i]){
    A[k++]=C[j++];//这里其实是逆序对
  }
}
  • 逆序对计数在归并过程中自然进行即可,无需提前对B/C数组做二分查找。

    二、代码实现

#include<cstdio>
#define MAX_NUM 4000006
typedef long long ll;
struct point{
  int x;
  int y;
};

point origin[MAX_NUM];
int arr[MAX_NUM];
ll count;//顺序对数目

void MergeSort_X(point* p,int lo,int hi);
void MergeSort_Y(int* p,int lo,int hi);
void Merge_X(point* p,int lo,int mi,int hi);
void Merge_Y(int* p,int lo,int mi,int hi);

int main(){
  int n;
  scanf("%d",&n );
  for(int i=0;i<n;i++){
    scanf("%d%d", &origin[i].x,&origin[i].y);
  }
  MergeSort_X(origin,0,n);//将origin数组按照X坐标升序排列
  for(int i=0;i<n;i++){
    arr[i]=origin[i].y;
  }
  MergeSort_Y(arr,0,n);//计算Y坐标中顺序对的数目
  printf("%lld",count);
}

//对数组p[lo,mi)按X坐标进行升序排序
void MergeSort_X(point* p,int lo,int hi){
  if(hi-lo<2)return;
  int mi=(lo+hi)/2;
  MergeSort_X(p,lo,mi);
  MergeSort_X(p,mi,hi);
  Merge_X(p,lo,mi,hi);
}

//对数组p[lo,mi)和数组p[mi,hi)进行归并
void Merge_X(point* p,int lo,int mi,int hi){
  point* A=p+lo;
  int lenB=mi-lo;//p[lo,mi)
  int lenC=hi-mi;//p[mi,hi)
  point* B=new point[lenB];
  for(int i=0;i<lenB;i++){
    B[i]=A[i];
  }
  point* C=p+mi;
  for(int i=0,j=0,k=0;i<lenB;){
    if(j>=lenC||B[i].x<=C[j].x)A[k++]=B[i++];
    if(j< lenC&&C[j].x< B[i].x)A[k++]=C[j++];
  }
  delete[] B;
}

void MergeSort_Y(int* p,int lo,int hi){
  if(hi-lo<2)return;
  int mi=(lo+hi)/2;
  MergeSort_Y(p,lo,mi);
  MergeSort_Y(p,mi,hi);
  Merge_Y(p,lo,mi,hi);
}

void Merge_Y(int* p,int lo,int mi,int hi){
  int* A=p+lo;
  int lenB=mi-lo;
  int lenC=hi-mi;
  int* B=new int[lenB];
  int* C=p+mi;
  for(int i=0;i<lenB;i++){
    B[i]=A[i];
  }
  for(int i=0,j=0,k=0;i<lenB;){
    if(j>=lenC||B[i]<C[j]){
      A[k++]=B[i++];
      if(j<lenC)count+=(lenC-j);//顺序对在这里出现
    }
    if(j< lenC&&C[j]<=B[i]){
      A[k++]=C[j++];//这里其实是逆序对
    }
  }
  delete[] B;
}

猜你喜欢

转载自www.cnblogs.com/cbw052/p/10467246.html