灯塔(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;
}