问题描述
设 p1(x1,y1),p2(x2,y2), …… ,pn(xn,yn)是平面上n个点构成的集合S,最近对问题就是找出集合S中距离最近的点对。
分治法思想
分治法总体来说分为三个步骤:划分、求解子问题、合并
这题的思路是,将集合S分为左右两个子集S1和S2,两个子集分别含有n/2个点,先在每个子集中递归地求其最接近的点对,再求最近的两点分别在两个集合中的点对,最后比较得出对短距离。
最近对问题的算法效率取决于划分点m的选取
如果选取m =(max{S}+min{S})/2,则有可能因集合S中点分布的不均匀而造成子集S1和S2的不平衡,如果使用各中点坐标的中位数(即S的中值)作为分割点,则会得到一个平衡的分割点,使得子集S1和S2中有个数大致相同的点。
暴力法代码:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iomanip>
void BF(const vector<Point>& v)
{
double ans = maxn;
int r1 = -1, r2 = -1;
int len = v.size();
for(int i=0; i<len; ++i)
{
for(int j=i+1; j<len; ++j)
{
double d = Dis(v[i],v[j]);
if(d<ans)
{
ans = d;
r1 = i, r2 = j;
}
}
}
cout<<"n^2暴力枚举的蛮力法:\n";
cout<<"最近的两个点为:"<<v[r1]<<"和"<<v[r2]<<endl;
cout<<"最近对的距离为:"<<ans<<endl;
}
int main()
{
//freopen("out.txt","w",stdout);
vector<Point> points;
srand(time(NULL));
int n = 15;
for(int i=0;i<n;i++)
{
Point point;
point.x = rand()%71;
point.y = rand()%61;
points.push_back(point);
}
BF(points);
return 0;
}
得到结果:
分治法代码:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iomanip>
using namespace std;
const int maxn = 0x3f3f3f3f;
struct Point
{
int x,y;
friend ostream & operator << (ostream & out,const Point& p);
Point(int _x=0,int _y=0):x(_x),y(_y){}
bool operator < (const Point& rhs)const{
return x < rhs.x;
}
};
ostream & operator << (ostream & out,const Point& p){
out<<"("<<setw(2)<<p.x<<","<<setw(2)<<p.y<<")";
return out;
}
bool cmp(const Point&a,const Point&b) //按x坐标排序
{
return a.x<b.x;
}
bool cmp2(const Point&a,const Point&b) //按y坐标排序
{
return a.y<b.y;
}
double Dis(Point a,Point b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double min(double a,double b,double c)
{
return min(min(a,b),c));
}
double ClosestPoint(vector<Point> points,int m,int n) //左闭右开
{
if(n-m<2) return maxn;
if(n-m==2)
{
return Dis(points[m],points[n-1]);
}
if(n-m==3)
{
double a = Dis(points[m],points[n-1]);
double b = Dis(points[m],points[n-2]);
double c = Dis(points[n-1],points[n-2]);
return min(a,b,c);
}
int mid = (m+n)/2;
int mm = points[mid].x;
double d1 = ClosestPoint(points,m,mid); // 左边区域最短距离
double d2 = ClosestPoint(points,mid,n); // 右边区域最短距离
double minn = min(d1,d2);
vector<Point> left,right;
for(int i=m;i<mid;++i)
{
if(points[i].x>mm-minn)
left.push_back(points[i]);
}
for(int i=mid;i<n;++i)
{
if(points[i].x<=mm+minn)
right.push_back(points[i]);
}
sort(right.begin(),right.end(),cmp2); //按y坐标排序
double mindist = 100000;
for(int i=0;i<(int)left.size();i++) //遍历左边所有点求与右边最短距离
{
for(int j=0;j<(int)right.size();j++)
{
if(abs(left[i].y-right[j].y)<minn)
{
double d = Dis(left[i],right[j]);
if(d<min) mindist = d;
}
}
}
return min(minn,mindist);
}
int main()
{
//freopen("out.txt","w",stdout);
vector<Point> points;
srand(time(NULL));
int n = 15;
for(int i=0;i<n;i++)
{
Point point;
point.x = rand()%71;
point.y = rand()%61;
// cin>>point.x>>point.y;
points.push_back(point);
}
sort(points.begin(),points.end(),cmp);
for(int i=0;i<n;i++)
{
//cout << points[i].x <<" "<< points[i].y <<endl;
cout<<points[i]<<endl;
}
cout << "分治法求得的答案为:"<< ClosestPoint(points,0,points.size()) << endl ;
return 0;
}
结果图为: