目的:
求两相离凸包最短距离。
工具:
- 点到线段最短距离
- 线段上点最短距离
- 将所有点顺时针排序
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <climits>
#include <iostream>
using namespace std;
const int MAX_N=10010;
const double INF=1e90;
const double eps=1e-10;
int n,m;
struct Point {
double x,y;
Point () {}
Point (double x,double y) : x(x),y(y){
}
Point operator + (const Point& rhs) const {
return Point(x+rhs.x,y+rhs.y);
}
Point operator - (const Point& rhs) const {
return Point(x-rhs.x,y-rhs.y);
}
Point operator * (const double d) const {
return Point(d*x,d*y);
}
double dot(const Point& rhs) const {
return x*rhs.x+y*rhs.y;
}
double cross(const Point& rhs) const {
return x*rhs.y-y*rhs.x;
}
double dis(const Point& rhs) const {
return sqrt((x-rhs.x)*(x-rhs.x)+(y-rhs.y)*(y-rhs.y));
}
}P[MAX_N],Q[MAX_N],center;
inline bool cmp(Point a,Point b)
{//a点在b点顺时针方向返回true,否则返回false
double res=(a-center).cross(b-center);
if(res<eps) return true;
if(res>eps) return false;
//向量共线,用距离判断
double d1=a.dis(center);
double d2=b.dis(center);
return d1>d2;
}
inline void Clockwise(Point point[],int num)
{//将所有点按照顺时针排序
//计算所有点重心
double x=0,y=0;
for(int i=0;i<num;i++){
x+=point[i].x;
y+=point[i].y;
}
center.x=x/num;
center.y=y/num;
sort(point,point+num,cmp);
}
inline double Get_Dis_Point(Point A,Point B,Point C)
{//计算C点到线段AB的最短距离
//AC.dot(AB)=|AC|*|AB|*cos(alpha),alpha是角BAC的大小,点积值小于0说明alpha>(PI/2)
//所以A离C更近
if((C-A).dot(B-A)<-eps) return A.dis(C);
if((C-B).dot(A-B)<-eps) return B.dis(C);
//考虑C到AB的垂足落在线段AB上的情况
//AB.cross(AC)=|AB|*|AC|*sin(alpha),其中alpha是角BAC的大小
return fabs((B-A).cross(C-A)/A.dis(B));
}
inline double Get_Dis_Segment(Point A,Point B,Point C,Point D)
{//求出线段AB和线段CD之间的最短距离,只需计算端点最短距离
double res=min(Get_Dis_Point(A,B,C),Get_Dis_Point(A,B,D));
res=min(res,Get_Dis_Point(C,D,A));
res=min(res,Get_Dis_Point(C,D,B));
return res;
}
inline double Get_Min_Dis(Point point1[],Point point2[],int num1,int num2)
{//获取两相离凸包的最短距离,point1[],point2[]和num1,num2分别是两凸包顶点数组及顶点数量
int ymin=0,ymax=0;
//获取一个凸包的y值最小点
for(int i=0;i<num1;i++){
if(point1[i].y<point1[ymin].y){
ymin=i;
}
}
//获取另一个凸包的y值最大点
for(int i=0;i<num2;i++){
if(point2[i].y>point2[ymax].y){
ymax=i;
}
}
point1[num1]=point1[0];
point2[num2]=point2[0];
double tmp,ans=point1[ymin].dis(point2[ymax]);
for(int i=0;i<num1;i++){
while(1){
tmp=(point2[ymax+1]-point1[ymin+1]).cross(point1[ymin]-point1[ymin+1])-
(point2[ymax]-point1[ymin+1]).cross(point1[ymin]-point1[ymin+1]);
if(tmp>eps) break;
ymax=(ymax+1)%num2;
}
if(tmp<-eps){ //只旋转到一条边上,计算点到线段距离
ans=min(ans,Get_Dis_Point(point1[ymin],point1[ymin+1],point2[ymax]));
}else { //同时旋转到两条边上
ans=min(ans,Get_Dis_Segment(point1[ymin],point1[ymin+1],point2[ymax],point2[ymax+1]));
}
ymin=(ymin+1)%num1;
}
return ans;
}
int main()
{
freopen("Kin.txt","r",stdin);
while(~scanf("%d%d",&n,&m)&&(n||m)){
for(int i=0;i<n;i++){
scanf("%lf%lf",&P[i].x,&P[i].y);
}
for(int j=0;j<m;j++){
scanf("%lf%lf",&Q[j].x,&Q[j].y);
}
Clockwise(P,n);//将顶点顺时针排序
Clockwise(Q,m);
double ans=min(Get_Min_Dis(P,Q,n,m),Get_Min_Dis(Q,P,m,n));
printf("%f\n",ans);
}
return 0;
}