http://acm.hdu.edu.cn/showproblem.php?pid=1007
题意: 给n个点,求他们两两之间的距离的最小值
使用分治解法, 此题位平面最近点对问题的模板题
将所有点按x排序,然后不断二分,直到每一个小区间内只有1~2个点
对于一个区间l,r,他们的中间值mid, 假如我们知道 l~mid mid+1~r 这两个区间里面点的最短距离d1,d2,那么l~r区间的最短距离d应该在 d1,d2,以及左右区间的点的距离的最小值组成。那么我们怎么算左右区间的点的距离的最小值呢,我们先让d取d1,d2中的最小值,然后朴素判断即可。(当然有方法简化运算,具体可以看代码)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<map>
#include<vector>
using namespace std;
const int maxn = 1e5 + 7;
const int INF = 0x3f3f3f3f;
int n,temp[maxn];
struct node {
double x, y;
}num[maxn];
bool cmp_xy(node a, node b) {
if (a.x != b.x) return a.x < b.x;
else return a.y < b.y;
}
bool cmp_x(node a, node b) {
return a.x < b.x;
}
bool cmp_y(int a, int b) {
return num[a].y < num[b].y;
}
double count(node a,node b) {
return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
}
double solve(int l,int r) { //含l,r
double d = INF;
if (l == r) return d;
if (r == l + 1) return count(num[l], num[r]);
int mid = (l + r) >> 1; //取中间点
double d1 = solve(l, mid);
double d2 = solve(mid + 1, r);
d = min(d1, d2);
int ind = 0; //下面求d
for (int i = l; i <= r; i++) {
if (fabs(num[i].x - num[mid].x) > d) continue; //先初步判断 ,将有可能的点存起来
else temp[++ind] = i;
}
sort(temp + 1, temp + 1 + ind, cmp_y); //可能的点再按y排序
for (int i = 1; i < ind; i++) {
for (int j = i + 1; j <= ind && num[temp[j]].y - num[temp[i]].y < d; j++) {
d = min(d, count(num[temp[i]], num[temp[j]]));
}
}
return d;
}
int main() {
while (scanf("%d", &n), n) {
for (int i = 1; i <= n; i++) {
scanf("%lf %lf", &num[i].x, &num[i].y);
}
sort(num + 1, num + n + 1, cmp_xy); // 先按x 排序,x一样排y
printf("%.2lf\n", solve(1, n)/2);
}
return 0;
}