分治法——平面最近点对问题
**问题:**寻找一个二维平面上的最近点对的距离
思路:
等分平面,分治左右,合并左右问题和跨分割线的解。
首先考虑一维的情况,假如将所有的点在x轴的某个区间上从小到大排列,以中位数为基准点将该区间分为点数量基本等同的两部分p1,p2,那么最近点对有三种可能:p1中的最近点对,p2中的最近点对,跨p1p2的最近点对,分别对应了分治法中的子问题和子问题合并。对于子问题而言,最小的子问题是区间内仅有一个或者两个点,即最近点对的距离为无限或者是两点距离。对于跨p1p2的最近点对,假如采用遍历的方法,那么时间复杂度达到了O(n2),优化一下,其实只需要找到左边区间的最大点就可以了,找到后计算与其右边的那个点的距离。设p1p2中最近点对的距离中较小的那个距离为d,区间分界线为m,那么只需判断(m-d,m]中是否有点就可以了(至多有一个,否则与最近点对距离为d矛盾,遍历左边的子区间n/2个点即可),复杂度达到了O(n)。
考虑二维的情况,同样的思路,在确定分跨两个部分的点的最近距离时,每个点最多只有六个对面的可能满足条件的解(证明略,画图看看即可),遍历左边的子区间n/2个点,每个点再与6个点计算距离,一共6*n/2次判断,复杂度同样是O(n)。如何确定这个点所对应的六个点呢?可以按照y坐标排序,对于每个点比较其y坐标序下的前后六个点。
关于分割线临界处的问题,采用x主排,y辅排,利用x和y共同划分左右区间。
代码:
#include <bits/stdc++.h>
using namespace std;
const double INF = 1e20;
struct Point{
double x;
double y;
}p[105];
int mp[105];
double dis(Point p1,Point p2){
return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
bool cmpy(const int &a,const int &b){
return p[a].y < p[b].y;
}
bool cmpxy(const Point &a,const Point &b){
if(a.x != b.x)
return a.x < b.x;
return a.y < b.y;
}
// 计算二维平面点集中的最近点对的距离
// x坐标区间为[p[l],p[r]]
// 输入:查找区间
// 输出:最近点对的距离
double nearestPair(int l,int r){
// 处理最小子问题
if(l==r){
return INF;
}else if(l+1 == r){
return dis(p[l],p[r]);
}else{
// 处理一般问题
// 划分子区间
int mid = (l+r)/2;
double d1 = nearestPair(l, mid);
double d2 = nearestPair(mid+1, r);
// 合并问题
// 分离出宽度为2d的子区间
double d = min(d1, d2);
int k = 0;
for(int i = l;i <= r;i++){
if(fabs(p[i].x-p[mid].x)<=d)
mp[k++] = i;
}
sort(mp, mp+k, cmpy);
// 在符合条件的点中进行选择
for(int i = 0;i < k;i++){
for(int j = i-6>=0?i-6:0;j <= i+6<k-1?i+6:k-1;j++){
if(i==j) continue;
if(!((p[i].x<=p[mid].x)&&(p[i].y<=p[mid].y) && (p[j].x>p[mid].x)&&(p[j].y>p[mid].y))) break;
double d3 = dis(p[i], p[j]);
cout<<p[i].x<<" "<<p[j].x<<endl;
if(d3<d) d = d3;
}
}
return d;
}
}
int main(){
p[0].x = 1.1;
p[1].x = 2.1;
p[2].x = 3.14;
p[3].x = 2.2;
p[4].x = 1.11;
p[5].x = 0.1;
p[6].x = -2.2;
p[7].x = -1.1;
p[0].y = 2.2;
p[1].y = 1.2;
p[2].y = 2.2;
p[3].y = -1.1;
p[4].y = 0.22;
p[5].y = 3.1;
p[6].y = 2.1;
p[7].y = 0.99;
sort(p, p+8, cmpxy);
cout<<nearestPair(0, 7);
return 0;
}