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;
}

原文地址:https://www.cnblogs.com/cbw052/p/10467246.html

时间: 2024-11-05 22:01:24

LightHouse/归并排序的相关文章

归并排序的利用

归并排序可以用其O(nlgn)的速度解决很多问题,另外在其归并的过程中,还可以做一些小动作. 例如:求一系列点[1,3,8,4,.....]的逆序对数,或正序对数.(两两比较左小于右为正序) 然后,就有了<灯塔问题>这个题目. /*  灯塔的坐标为(x,y),且各不相同,灯塔处于另外一个灯塔的东北角90度范围,或西南角90度范围内,才能互相照射到. /*  给定N个(x,y)坐标,求能互相照射到的对数.(网上可以搜索到此题.) Input 3 2 2 4 3 5 1 Output 1 解答:此

灯塔(LightHouse)

Description As shown in the following figure, If another lighthouse is in gray area, they can beacon each other. For example, in following figure, (B, R) is a pair of lighthouse which can beacon each other, while (B, G), (R, G) are NOT. Input 1st lin

算法 排序NB二人组 堆排序 归并排序

参考博客:基于python的七种经典排序算法     常用排序算法总结(一) 序前传 - 树与二叉树 树是一种很常见的非线性的数据结构,称为树形结构,简称树.所谓数据结构就是一组数据的集合连同它们的储存关系和对它们的操作方法.树形结构就像自然界的一颗树的构造一样,有一个根和若干个树枝和树叶.根或主干是第一层的,从主干长出的分枝是第二层的,一层一层直到最后,末端的没有分支的结点叫做叶子,所以树形结构是一个层次结构.在<数据结构>中,则用人类的血统关系来命名,一个结点的分枝叫做该结点的"

codeforces 414C C. Mashmokh and Reverse Operation(归并排序求逆序对)

题目链接: C. Mashmokh and Reverse Operation time limit per test 4 seconds memory limit per test 512 megabytes input standard input output standard output Mashmokh's boss, Bimokh, didn't like Mashmokh. So he fired him. Mashmokh decided to go to university

归并排序(Merge Sort)

更详细的请看这篇博文:http://www.cnblogs.com/jingmoxukong/p/4308823.html 我只做个人学习和补充 基于分治法的排序,比较简单.个人感觉其核心是数组左右拆分之后类似队列的比较.类似两两合并之类的算法都可以参考 分为3个步骤,拆开为树结构遍历->向上合并 特别做了一张gif来更好的理解归并排序 左边集合比较通过就左边索引前进,右边集合比较通过则右边索引前进 比较结果放入结果数组中 最后如果多出一个就把这一个直接加入结果数组 为了易于学习,代码直接使用了

两路归并排序

链表两路归并 #include<iostream> #include<assert.h> using namespace std; struct node { int val; node * next; node(int v) { val=v; next=NULL; } }; node * merge(node* list1 , node * list2) { assert(list1!=NULL&&list2!=NULL);//括号中是希望出现的正确的情况  no

算法基础:分治模式,归并排序ΘΘΘΘΘΘ知识小结

1.分治模式在每层递归时都有三个步骤:分解,解决,合并 2.归并排序算法完全遵循分治模式: 分解:分解待排序的n个元素的序列成各具n/2个元素的两个子序列 解决:使用归并排序递归的排序两个子序列 合并:合并两个已排序的子序列以产生已排序的答案 3.分析分治算法所需要的时间计算: 假设T(n)是规模为n的一个问题的运行时间,若问题足够小,如对某个常量c,n≦c,则直接求解需要常量时将,我们将其写作Θ(1).假设吧原问题分解成a个子问题,每个子问题的规模是原问题的1/b(对归并排序,a和b都为2,然

归并排序

基本思想: 1)首先需要一个临时空间temp用来存放合并后的元素,长度为排列好的元素总个数. 2)取两个指针,分别为左右部分数组的头元素. 3)比较指针指向元素,将较小(较大)的那个放入temp(注意相等时不做元素位置交换),并将两指针向后移动一个step. 4)循环步骤3直到其中一段数组没有剩余元素(其中一段被遍历完). 5)将还有元素剩余的另外一半数组元素直接放入temp尾部 代码实现: public static void merge(int[] list, int left, int m

基本排序算法(冒泡排序 选择排序 插入排序 快速排序 归并排序 基数排序 希尔排序)

冒泡排序 public static void bubbleSort(int[] arr){ int lgn = arr.length; for (int i = 0; i < lgn - 1; i++) { for (int j = 0; j < lgn - 1 - i; j++) { if(arr[j] > arr[j + 1]){ int temp = arr[j + 1]; arr[j + 1] = arr[j]; arr[j] = temp; } } } } 选择排序 publ