在n(n>1) 个点的集合中寻找最近点对。即求任意两点的欧几里得距离的最小值。
1)最简单的暴力搜索算法,时间复杂对为O(n*n)。
2)这里主要考虑分治算法,运行时间的递归式为T(n) = 2*T(n/2)+O (n),时间复杂度为O(n*lgn)。
算法思想:
将集合中的点按x坐标排序,我们可以想象一条垂直的直线将集合划分为左右两部分,对于每一部分,我们可以递归的求出最小距离,最终得到的两个值(dl, dr)并不一定是解,因为忽略了跨越垂线的亮点的距离(d)。所以我们必须再搜索一遍两部分的点。但不需要全部搜索,那样无疑就是第一种算法了。假设md = min (dl, dr,),搜索的点要满足坐标x的值
与中间坐标x值的差小于md。对于搜索出的这些点也不是都必须计算距离,如果彼此y坐标的值已经大于md的话,就不需要计算了。说的模模糊糊的,看代码吧:
#include <iostream>
#include <sstream>
#include <string>
#include <queue>
#include <stack>
#include <vector>
#include <list>
#include <map>
#include <algorithm>
#include <numeric>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <set>
#include <deque>
#include <bitset>
#include <functional>
#include <utility>
#include <iomanip>
#include <cctype>
using namespace std;
#define FOR(i,a,b) for(i = (a); i < (b); ++i)
#define FORE(i,a,b) for(i = (a); i <= (b); ++i)
#define FORD(i,a,b) for(i = (a); i > (b); --i)
#define FORDE(i,a,b) for(i = (a); i >= (b); --i)
#define max(a,b) ((a) > (b)) ? (a) : (b)
#define min(a,b) ((a) < (b)) ? (a) : (b)
#define CLR(a,b) memset(a,b,sizeof(a))
#define PB(x) push_back(x)
typedef long long LL;
typedef vector<int> VI;
const int MAXN = 100009;
const int MAXM = 0;
const int hash_size = 25000002;
const int INF = 0x7f7f7f7f;
map<string, int> mp;
map<string, int>::iterator it;
typedef struct {
double x, y;
}Point;
typedef struct {
double x, y;
int index;
}TPoint;
Point px[MAXN];
int n;
double ans;
void closest(TPoint py[], int low, int high);
void closest_pair();
double dist(Point a, Point b);
int compareX(Point a, Point b);
int compareY(TPoint a, TPoint b);
int main(void) {
int i;
while (scanf("%d", &n) != EOF && n) {
ans = INF;
FOR(i, 0 , n)
scanf("%lf %lf", &px[i].x, &px[i].y);
closest_pair();
printf("%.2lf\n", ans/2);
}
return 0;
}
void closest_pair(){
TPoint *py = new TPoint[n+5];
sort(px, px+n, compareX); // 对x数组进行递增排序
for( int i = 0 ; i < n ;i ++){
py[i].index = i;
py[i].x = px[i].x;
py[i].y = px[i].y;
}
sort(py, py+n, compareY); // 对y数组进行递增排序
closest(py, 0, n-1); // 求亲密点对
ans = sqrt(ans);
delete py;
}
void closest(TPoint py[], int low, int high){
int i,j,k,m;
double dl, dr, d;
if((high-low)==1){ // 当n=2时直接计算
ans = min(ans, dist(px[low], px[high]));
}
else{
if((high-low)==2){ // 当n=3时直接计算
dl = dist(px[low], px[low+1]);
dr = dist(px[low], px[low+2]);
d = dist(px[low+1], px[low+2]);
ans = min(ans, dl);
ans = min(ans, dr);
ans = min(ans, d);
}
else{ // 当n>3时进行分治
TPoint *SL = new TPoint[(high-low)/2+10];
TPoint *SR = new TPoint[(high-low)/2+10];
m = (high-low)/2 + low; // 把x数组以m为界划分为两半
j = k = 0;
for(i=0; i<=high-low; i++){
if(py[i].index<=m){
SL[j++] = py[i]; // 收集左边子集中的最近点对
}
else{
SR[k++] = py[i]; // 收集右边子集中的最近点对
}
}
closest(SL, low, m); // 计算左边子集的最近点对
closest(SR, m+1, high);// 计算右边子集的最近点对
Point *Z = new Point[high-low+10];
k = 0;
for( i=0; i<=high -low; i++){ // 收集距离中线距离小于d的元素,保存到数组Z(因Y数组按y坐标递增排序,Z数组也一样)
if(fabs(px[m].x - py[i].x)<ans){
Z[k].x = py[i].x, Z[k++].y = py[i].y;
}
}
for( i=0; i<k; i++){
for( j=i+1; (j<k)&&(Z[j].y-Z[i].y<ans); j++){ // 若前后两点y轴的距离超过d则不可能使距离小于d,退出
dl = dist(Z[i], Z[j]); // 计算前后两点的距离
if(dl<ans){ // 若小于d,则更新
ans = dl;
}
}
}
delete SL;
delete SR;
delete Z;
}
}
}
int compareX(Point a, Point b)
{
return a.x < b.x;
}
int compareY(TPoint a, TPoint b)
{
return a.y < b.y;
}
double dist(Point a, Point b){
double dx, dy;
dx = a.x - b.x, dy = a.y - b.y;
return (dx*dx+dy*dy);
}
1982

被折叠的 条评论
为什么被折叠?



