G55 平面最近点对 分治算法

 视频链接:https://www.bilibili.com/video/BV1ko4y1s7Ed/

1. Luogu P1257 平面上的最接近点对

2. Luogu P1429 平面最近点对(加强版)

题意:给定平面上 n 个点,求最近点对的距离

思路:

  1. 对所有点按 x 为第一关键字,y 为第二关键字进行排序
  2. 分治求出中线两边的最小距离d1和d2,则 d=min(d1,d2)
  3. 划出中线左右距离为 d 的平行区域,搜集区域内的点;按纵坐标排序;检查纵坐标之差小于 d 的情况,更新 d 的值

时间:O(nlogn*logn)

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;

const int N = 200010;
struct Point{double x,y;} a[N],b[N];

bool cmp(Point &a,Point &b){
  return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
bool cmpy(Point &a,Point &b){
  return a.y<b.y;
}
double dis(Point &a,Point &b){
  return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double devide(int l,int r){
  if(l==r) return 2e9;                      //单点返回∞
  if(l+1==r) return dis(a[l],a[r]);         //邻点返回dis
  int mid=(l+r)>>1;
  double d=min(devide(l,mid),devide(mid+1,r));//中线两边取最小
  
  int k=0;    //跨中线处理
  for(int i=l;i<=r;i++)   //到中线的水平距离<d
    if(fabs(a[i].x-a[mid].x)<d) b[++k]=a[i];
  sort(b+1,b+k+1,cmpy);   //按y排序
  for(int i=1;i<k;i++)    //两点竖直距离<d
    for(int j=i+1;j<=k&&b[j].y-b[i].y<d;j++)
      d=min(d,dis(b[i],b[j]));
  return d; 
}
int main(){
  int n; cin>>n;
  for(int i=1;i<=n;i++)scanf("%lf%lf",&a[i].x,&a[i].y);
  sort(a+1,a+n+1,cmp);    //按(x,y)排序
  printf("%.4lf\n",devide(1,n));
}

 

// 分治+归并排序
// O(nlogn)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;

const int N=200010;
struct Point{double x,y;} a[N],b[N];

bool cmp(Point &a,Point &b){
  return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
double dis(Point &a,Point &b){
  return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double divide(int l,int r){
  double d=2e9;
  if(l==r) return d;
  int m=(l+r)>>1; Point tmp=a[m];  
  d=min(divide(l,m),divide(m+1,r));
  
  int i=l,j=m+1,k=0,t=0;
  while(i<=m && j<=r)
    if(a[i].y<a[j].y) b[k++]=a[i++];
    else b[k++]=a[j++];
  while(i<=m) b[k++]=a[i++];
  while(j<=r) b[k++]=a[j++];
  for(i=l,j=0; i<=r;) a[i++]=b[j++];
  
  for(i=0;i<k;i++)
    if(fabs(tmp.x-b[i].x)<d) b[t++]=b[i];

  for(i=0;i<t;i++)
    for(j=i+1;j<t&&b[j].y-b[i].y<d;j++)
      d=min(d,dis(b[j],b[i]));
  return d;
}
int main(){
  int n; cin>>n;
  for(int i=1;i<=n;i++)scanf("%lf%lf",&a[i].x,&a[i].y);
  sort(a+1,a+n+1,cmp);    //按(x,y)排序
  printf("%.4lf\n",divide(1,n));
}

 

// 多重有序集
// O(nlogn)
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <set>
using namespace std;

const int N=200005;
int n;
double ans=1e20;

struct Point{double x, y;};
struct cmp{
  bool operator()(const Point &a, const Point &b)const{
    return a.x<b.x || (a.x==b.x&&a.y<b.y);
  }
};
struct cmp_y{
  bool operator()(const Point &a, const Point &b)const{
    return a.y<b.y; 
  }
};
void update(const Point &a, const Point &b){
  ans=min(ans,sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)));
}
Point a[N];
multiset<Point, cmp_y> s; //多重有序集

int main(){
  scanf("%d", &n);
  for(int i=0; i<n; i++) scanf("%lf%lf", &a[i].x, &a[i].y);
  sort(a, a+n, cmp());
  for(int i=0, l=0; i<n; i++){
    while(l<i && a[i].x-a[l].x>=ans) s.erase(s.find(a[l++]));
    for(auto it=s.lower_bound({a[i].x, a[i].y-ans}); 
             it!=s.end() && it->y-a[i].y<ans; it++) update(*it, a[i]);
    s.insert(a[i]);
  }
  printf("%.4lf", ans);
  return 0;
}

 

posted @ 2023-02-27 15:57  董晓  阅读(613)  评论(1编辑  收藏  举报