最近对问题:
最近对问题要求在一个包含n个点的集合中找出距离最近的两个点。严格地讲,距离最近的点可能多于一个,简单起见,只找出其中的一对即可。
最近对问题的分治策略如下:
(1)划分:将集合S分成两个子集S1和S2,根据平衡子问题原则,每个子集中大约有n/2个点,设集合S的最近点对是
p
i
\displaystyle p_i
pi和
p
j
\displaystyle p_j
pj(1
≤
\displaystyle\leq
≤i,j
≤
\displaystyle\leq
≤n),则会出现以下三种情况:
1.
p
i
∈
S
1
\displaystyle p_i\in S1
pi∈S1,
p
j
∈
S
1
\displaystyle p_j\in S1
pj∈S1,即最近点对均在集合S1中;
2.
p
i
∈
S
2
\displaystyle p_i\in S2
pi∈S2,
p
j
∈
S
2
\displaystyle p_j\in S2
pj∈S2,即最近点对均在集合S2中;
3.
p
i
∈
S
1
\displaystyle p_i\in S1
pi∈S1,
p
j
∈
S
2
\displaystyle p_j\in S2
pj∈S2,即最近点对均在集合S1中;
(2)求解子问题,对于划分阶段的情况1和情况2可递归求解,如果最近点对分别在S1和S2中,问题就有些复杂了。
(3)合并:比较在划分阶段三种情况下的最近点对,取三者中的距离较小者为原问题的解。
C++实现:
#include<iostream>
#include<cmath>
using namespace std;
struct point
{
int x;
int y;
};
int Partition(point S[],int first,int end)
{
int i=first,j=end;
while(i<j)
{
while(i<j&&S[i].y<=S[j].y) j--;
if(i<j)
{
point temp;
temp=S[i];
S[i]=S[j];
S[j]=temp;
i++;
}
while(i<j&&S[i].y<=S[j].y) i++;
if(i<j)
{
point temp;
temp=S[i];
S[i]=S[j];
S[j]=temp;
j--;
}
}
return i;
}
void QuickSort(point S[],int first,int end)
{
int pivot;
if(first<end)
{
pivot=Partition(S,first,end);
QuickSort(S,first,pivot-1);
QuickSort(S,pivot+1,end);
}
}
double Distance(point a,point b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double Closest(point S[],int low,int high)
{
double d1,d2,d3,d;
int mid,i,j,index;
point P[1000]; //存放集合P1和P2
if(high-low==1) //只有两个点,返回两点间的距离
return Distance(S[low],S[high]);
if(high-low==2) //只有三个点,求最近点对距离
{
d1=Distance(S[low],S[low+1]);
d2=Distance(S[low+1],S[high]);
d3=Distance(S[low],S[high]);
if((d1<d2)&&(d1<d3))
return d1;
else if(d2<d3)
return d2;
else
return d3;
}
mid=(low+high)/2;
d1=Closest(S,low,mid); //递归求解子问题1
d2=Closest(S,mid+1,high); //递归求解子问题2
if(d1<=d2) d=d1; //求解子问题3,最近点对在两个集合中
else d=d2;
index=0;
for(i=mid;(i>=low)&&(S[mid].x-S[i].x<d);i--) //建立点集合P1
P[index++]=S[i];
for(i=mid+1;(i<=high)&&(S[i].x-S[mid].x<d);i++); //建立点集合P2
QuickSort(P,0,index-1); //对集合P1,P2按y坐标升序排列
for(i=0;i<index;i++) //依次处理集合P1,P2中的点
{
for(j=i+1;j<index;j++)
{
if(P[i].y-P[i].y>=d) //超出y的范围,点P[i]处理完毕
break;
else
{
d3=Distance(P[i],P[j]);
if(d3<d)
d=d3;
}
}
}
return d;
}
int main()
{
struct point S[1000];
int n;
cin>>n;
for(int i=0;i<n;i++)
cin>>S[i].x>>S[i].y;
for(int i=0;i<n-1;i++)
{
for(int j=0;j<n-i-1;j++)
if(S[j].x>S[j+1].x)
{
point temp=S[j+1];
S[j+1]=S[j];
S[j]=temp;
}
}
cout<<Closest(S,0,n-1);
}