模板--二维凸包(2--两相离凸包最短距离)

目的:
求两相离凸包最短距离。
工具:

  • 点到线段最短距离
  • 线段上点最短距离
  • 将所有点顺时针排序
#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值