poj 3608 旋转卡壳求两凸包间的最近距离

旋转卡壳的确很卡人。现在本人学旋转卡壳还在初级阶段。但是这是算法的效率的确很高,所有必需得学会
我想说的是旋转卡壳求凸包的最近点对距离。大体思路如下:
        对于给定的点,调整为逆时针方向;
        一个P凸包中找纵坐标最小的点,在另一个Q凸包中找纵坐标最大的点;
        进行卡壳处理,具体处理应为这样:
              在p中纵坐标的最小点与之相邻的下标大一点与Q中纵坐标最大点组成三角形,再与Q中纵坐标最大点相邻的点下标大一的点组成另一个三角形。判断这两个三角形的面积的大小,进行旋转卡壳;如果卡不下去,就求距离,求距离时,分情况来求,可能是点到直线的距离,也可能卡壳的两条线平行,这样就直线间的距离。
求点到直线的距离,还有就是线到线段距离时候,需要处理一下,我觉得对我来说,这是难点。
代码如下,做的时候,叉积一直搞错,得注意...TT....

#include<stdio.h>
#include<iostream>
#include<math.h>
#include<algorithm>
using namespace std;
#define N 10002
#define EPS 1e-10
struct point
{
    double x;
    double y;
}pn[N],pm[N];
double dis(point a,point b)
{
    return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
double det(double x1,double y1,double x2,double y2)
{
    return x1*y2-x2*y1;
}
double cross(point a,point b,point c)
{
    return det(a.x-c.x,a.y-c.y,b.x-c.x,b.y-c.y);
}
double dot(point a,point b)
{
    return a.x*b.x+a.y*b.y;
}
double min(double a,double b)
{
    return a>b?b:a;
}
void adjust(point *p,int n)
{
    for(int i=0;i<n-2;i++)
    {
        int res=cross(p[i+1],p[i+2],p[i]);
        if(res>EPS)return;
        else if(res<-EPS)
        {
            reverse(p,p+n);
            return;
        }
    }
}
double point_to_seg(point a,point b,point c)
{
    point ab,ac;
    ab.x=b.x-a.x;
    ab.y=b.y-a.y;
    ac.x=c.x-a.x;
    ac.y=c.y-a.y;
    double f=dot(ab,ac);
    if(f<0)return dis(a,c);
    double f1=dot(ab,ab);
    if(f>f1)return dis(b,c);
    f=f/f1;
    point d;
    d.x=a.x+ab.x*f;
    d.y=a.y+ab.y*f;
    return dis(d,c);
}
double seg_to_seg(point a1,point b1,point a2,point b2)
{
    return min(min(point_to_seg(a1,b1,a2),point_to_seg(a1,b1,b2)),min(point_to_seg(a2,b2,a1),point_to_seg(a2,b2,b1)));
}
double rc(point *p1,int n,point *p2,int m)
{
    int s1=0,s2=0;
    for(int i=0;i<n;i++)
    {
        if(p1[i].y-p1[s1].y<-EPS)
        {
            s1=i;
        }
    }
    for(int i=0;i<m;i++)
    {
        if(p2[i].y-p2[s2].y>EPS)
        {
            s2=i;
        }
    }
    p1[n]=p1[0];
    p2[m]=p2[0];
    double res,ans=1e20;
    for(int i=0;i<n;i++)
    {
        while((res=cross(p2[s2],p1[s1+1],p1[s1])-cross(p2[s2+1],p1[s1+1],p1[s1]))>EPS)
        {
            s2=(s2+1)%m;
        }
        if(res<-EPS)ans=min(ans,point_to_seg(p1[s1],p1[s1+1],p2[s2]));
        else ans=min(ans,seg_to_seg(p1[s1],p1[s1+1],p2[s2],p2[s2+1]));
        s1=(s1+1)%n;
    }
    return ans;
}
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0&&m==0)break;
        for(int i=0;i<n;i++)
        {
            scanf("%lf%lf",&pn[i].x,&pn[i].y);
        }
        for(int i=0;i<m;i++)
        {
            scanf("%lf%lf",&pm[i].x,&pm[i].y);
        }
        adjust(pn,n);
        adjust(pm,m);
        printf("%.5lf\n",sqrt(min(rc(pn,n,pm,m),rc(pm,m,pn,n))));
    }
    return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值