【Openjudge】最近点对问题

平面最近点对问题,经典分治问题。

按照点的x坐标排序做预处理。

点集合s按照x坐标平分为l,r两组点,分别得到每组组内点的最小距离dl和dr,d = min(dl,dr),然后记横坐标距分割线距离小于d的点集为r,计算r内点与点的距离,与d比较,得到s内点的最小距离。

细节再与计算r内点与点的距离。

再从l组和r组得到分治结果后,对l和r内的点对y进行归并排序(复杂度为n),并筛查出属于r的点,这个排序后的序列记为R。这时候,有个trick。

任意一个点p,只要和他再R内的之前6个,之后6个点计算距离并比较,就足够了。证明参见《算法设计与分析》(屈婉玲)。


#include<iostream>
#include<algorithm>
#include<cmath>
#include<stdio.h>
#include<cstring>

using namespace std;

struct point{
  int x,y;
  int idx;
};
point temp[201000];

double distance(point a, point b)
{
  return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2));
}
double min_dis(point a[], point aux[], int l, int r)
{
  double delta = 1e10 * 2e10;
  if (l == r){
//    printf("%d %d %lf\n", l, r, delta);
    return delta;
  }
  int mid = (l + r)/2;
  delta = min(min(delta, min_dis(a, aux, l, mid)), min_dis(a, aux, mid + 1, r));
  int lp = l, rp = mid + 1, auxp = l;
  while (lp <= mid && rp <= r){
    if (a[lp].y <= a[rp].y){
      aux[auxp ++] = a[lp ++];
    }
    else{
      aux[auxp++] = a[rp ++];
    }
  }
  while (lp <= mid){
    aux[auxp ++] = a[lp ++];
  }
  while (rp <= r){
    aux[auxp ++] = a[rp ++];
  }
  int t = 0;
  for (int i = l; i <= r; i ++){
    if (abs(aux[i].x - a[mid].x) < delta){
      temp[t ++] = aux[i];
    }
  }
  for (int i = 0; i < t; i ++){
    for (int j = 1; j <= 6 && (i + j) < t; j ++){
      delta = min(delta, distance(temp[i], temp[i + j]));
    }
    for (int j = -1; j >= -6 && (i + j ) >= 0; j --){
      delta = min(delta, distance(temp[i], temp[i + j]));
    }
  }
  memcpy(&a[l], &aux[l], sizeof(point) * (r - l + 1));
//  printf("%d %d %lf\n", l, r, delta);

  return delta;
}

int main(){
  int n;
  struct point a[201000] = {}, aux[201000] = {};
  while (cin >> n){
    for (int i = 0; i < n; i ++){
      scanf("%d %d", &a[i].x, &a[i].y);
    }
    sort(a, a + n, [](point p1, point p2)->bool {return p1.y < p2. y;});
    for (int i = 0; i < n; i ++){
      a[i].idx = i;
    }
    double dis = min_dis(a, aux, 0, n - 1);
    printf("%.3lf", dis);
  }
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值