题目连接
题意:
找到平面内,最近两个点间距离, 最后答案除以2
数据范围:
N (2 <= N <= 100,000) N 个点,
思路:
如果暴力解法O()必然是 所以应该有 O()算法。
O()树,分治,等等,这题应该用分治算法。
1)既然用分治,就应该吧问题分成几份,先分两份试试。(为什么分治算法能优化呢 1 )分解问题时是树形结构。一分为2 一分为3.....。2)子问题单独求解后向上合并,可以扔掉一些不需要比较的点)。
2)按照 点的 横坐标 x 排序 (当然可以y),然后找到中间的点,把问题一分为2个子问题,然后继续找到左边区间 距离最近的 两点找到,右边区间 距离最近的两点。比较大小,然后合并左右区间,
3) 但是还有可能的是最近点对中的两点分属这两个集合.。所以我们需要再算一些点,
4)那我们合并是应该算那些点呢?求出两个子问题的解后,还没有合并(ans = 两个子问题中 两点最近的值),然后合并后我们应该从合并后的区间中线找到 距离中线水平距离不超过 ans 的点,(要是距离中线点得水平距离都超过 ans了,那我们肯定不用),然后把这些点再放到一个数组内进行求解,求出距离小于 ans 的点.
5)优化:如果两个之间y轴间的距离大于ans那我们也不再比较,这样最后合并之后得出的一部后按照y点排序,减少比较次数。
AC:
#include<iostream>
#include<cstring>
#include<stdio.h>
#include<math.h>
#include<vector>
#include<algorithm>
using namespace std;
const double esp = 1e-8;
#define Equ(a, b) (fabs((a) - (b)) < (eps))
#define Less(a, b) ((a) - (b) < (-esp)) //a < b
const int maxn = 100005;
struct type {
double x, y;
} point[maxn];
bool cmp_x(type a, type b) {
return a.x < b.x;
}
bool cmp_y(type a, type b) {
return a.y < b.y;
}
double dis (type a, type b) {
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
double solve (int left, int right) {
if(right - left == 1) return dis(point[left], point[right]); //只有两点
else if(right - left == 2) { //有3个点
double minn = min(dis(point[left], point[right]), dis(point[left+1], point[right]));
minn = min(minn, dis(point[left], point[left+1]));
return minn;
}else {//3个点以上
int mid = (left + right) >> 1;
double ans = min(solve(left, mid), solve(mid + 1, right));
int cnt = 0;
vector<type> P;
for(int i = left; i <= right; ++i) {
if(abs(point[i].x - point[mid].x) <= ans)
P.push_back(point[i]);
}
sort(P.begin(), P.end(), cmp_y);
for(int i = 0; i < P.size(); ++i) {
for(int j = i + 1; j < P.size(); ++j) {
if(P[j].y - P[i].y > ans) break;
ans = min(ans, dis(P[i], P[j]));
}
}
return ans;
}
}
int main() {
int N;
while(scanf("%d", &N) && N){
for(int i = 0; i < N; ++i) {
scanf("%lf %lf",&point[i].x, &point[i].y);
}
sort(point, point + N, cmp_x);
printf("%.2lf\n", solve(0,N-1)/2);
}
return 0;
}