POJ3608-旋转卡壳

来源:互联网 发布:java堆内存溢出 编辑:程序博客网 时间:2024/05/22 06:49

题目:题目链接

 

题意:给出两个多边形,求这两个多边形的最近距离

 

分析:旋转卡壳

 

 

学习旋转卡壳:

如果多边形相交, 那么最小距离就变得没有意义了。 然而, 这个问题的另一个版本, 凸多边形顶点对间最小距离对

于相交和非相交的情况都有解存在。回到我们的主问题: 直观的, 确定最小距离的点不可能包含在多边形的内部。

与最大距离问题相似, 我们有如下结论:

两个凸多边形 P 和 Q 之间的最小距离由多边形间的对踵点对确立。 存在凸多边形间的三种多边形间的对踵点对, 因

此就有三种可能存在的最小距离模式:“顶点-顶点”的情况 “顶点-边”的情况 “边-边”的情况

换句话说, 确定最小距离的点对不一定必须是顶点。 下面的三个图例表明了以上结论: 

    

给定结果, 一个基于旋转卡壳的算法自然而然的产生了: 

考虑如下的算法, 算法的输入是两个分别有 m 和 n 个顺时针给定顶点的凸多边形 P 和 Q。 计算 P 上 y 坐标值

最小的顶点(称为 yminP ) 和 Q 上 y 坐标值最大的顶点(称为 ymaxQ)。 为多边形在 yminP 和 ymaxQ 处构造

两条切线 LP 和 LQ 使得他们对应的多边形位于他们的右侧。 此时 LP 和 LQ 拥有不同的方向, 并且 yminP 和

ymaxQ 成为了多边形间的一个对踵点对。 计算距离(yminP,ymaxQ) 并且将其维护为当前最小值。 顺时针同时旋转平

行线直到其中一个与其所在的多边形的边重合。 如果只有一条线与边重合, 那么只需要计算“顶点-边”对踵点对

和“顶点-顶点”对踵点对距离。 都将他们与当前最小值比较, 如果小于当前最小值则进行替换更新。 如果两条切

线都与边重合, 那么情况就更加复杂了。 如果边“交叠”, 也就是可以构造一条与两条边都相交的公垂线(但是

在顶点处相交), 那么就计算“边-边”距离。 否则计算三个新的“顶点-顶点”对踵点对距离。 所有的这些距离

都与当前最小值进行比较, 若小于当前最小值则更新替换。 
重复执行步骤。直到新的点对为(yminP,ymaxQ)。 

输出最大距离。 

旋转卡壳模式保证了所有的对踵点对(和所有可能的子情况)都被考虑到。 此外, 整个算法拥有现行的时间复杂

度, 因为(除了初始化), 只有与顶点数同数量级的操作步数需要执行。

最小距离和最大距离的问题表明了旋转卡壳模型可以用在不同的条件下(与先前的直径和宽度问题比较)。 这个模

型可以应用于两个多边形的问题中。 

“最小盒子”问题(最小面积外接矩形)通过同一多边形上两个正交切线集合展示了另一种条件下旋转卡壳的应用。

#include<stdio.h>#include<string.h>#include<math.h>#include<algorithm>using namespace std;#define N 10015#define PR 1e-8struct TPoint{    double x,y;    TPoint(){};    TPoint(double _x,double _y):x(_x),y(_y){}    TPoint operator-(const TPoint p){return TPoint(x-p.x,y-p.y);}}s;struct TPolygon{    int n;    TPoint p[N];}ply1,ply2;int dblcmp(double a) {return fabs(a)<PR?0:a>0?1:-1;}double MIN(double a,double b) {return a<b?a:b;}double dist(TPoint a,TPoint b)//两点距{    TPoint c(a-b);    return sqrt(c.x*c.x+c.y*c.y);}double cross(TPoint a,TPoint b,TPoint c)//叉积{    TPoint s(b-a),t(c-a);    return s.x*t.y-s.y*t.x;}bool cmop(TPoint a,TPoint b)//x、y排序{    if(fabs(a.x-b.x)<PR) return a.y<b.y;    else return a.x<b.y;}bool cmp(TPoint a,TPoint b)//叉积内排序{    int d1=dblcmp(cross(s,a,b));    return d1>0||(d1==0&&dist(s,a)<dist(s,b));}TPolygon graham(TPolygon ply)//凸包{    int i,j,k;    sort(ply.p,ply.p+ply.n,cmop);    s=ply.p[0]; k=2;    sort(ply.p+1,ply.p+ply.n,cmp);    for(i=2,k=2;i<ply.n;i++)    {        while(k>1&&(dblcmp(cross(ply.p[k-2],ply.p[i],ply.p[k-1])))>=0) k--;        ply.p[k++]=ply.p[i];    }    ply.n=k;    return ply;}double disps(TPoint a,TPoint b,TPoint c)//点到线段的最近距离{    TPoint s(a-b),t(c-b);    if(s.x*t.x+s.y*t.y<0) return dist(a,b);    s=(a-c); t=(b-c);    if(s.x*t.x+s.y*t.y<0) return dist(a,c);    return fabs(cross(a,b,c))/(dist(b,c));}double disss(TPoint a,TPoint b,TPoint c,TPoint d)//两条线段最近距离{    return MIN(MIN(disps(a,c,d),disps(b,c,d)),MIN(disps(c,a,b),disps(d,a,b)));}double solve(TPolygon pl,TPolygon pr,int p,int q)//旋转卡壳,求凸边形最近距离{    int i;    double tmp,min=1e90;    pl.p[pl.n]=pl.p[0]; pr.p[pr.n]=pr.p[0];    for(i=0;i<pl.n;i++)    {        while((tmp=cross(pl.p[p+1],pr.p[q+1],pl.p[p])-cross(pl.p[p+1],pr.p[q],pl.p[p]))>PR)            q=(q+1)%pr.n;        if(tmp<-PR) min=MIN(min,disps(pr.p[q],pl.p[p],pl.p[p+1]));        else min=MIN(min,disss(pl.p[p],pl.p[p+1],pr.p[q],pr.p[q+1]));        p=(p+1)%pl.n;    }    return min;}void gettop(TPolygon pl,TPolygon pr,int &l,int &r)//得到最下和最上点{    int i;    l=r=0;    for(i=0;i<pl.n;i++)    {        if((dblcmp(pl.p[i].y-pl.p[l].y))<0) l=i;    }    for(i=0;i<pr.n;i++)    {        if((dblcmp(pr.p[i].y-pr.p[r].y))>0) r=i;    }}int main(){    while(scanf("%d%d",&ply1.n,&ply2.n),ply1.n&&ply2.n)    {        int i,l,r;        for(i=0;i<ply1.n;i++) scanf("%lf%lf",&ply1.p[i].x,&ply1.p[i].y);        for(i=0;i<ply2.n;i++) scanf("%lf%lf",&ply2.p[i].x,&ply2.p[i].y);        ply1=graham(ply1);        ply2=graham(ply2);        gettop(ply1,ply2,l,r);        double mindis=MIN(solve(ply1,ply2,l,r),solve(ply2,ply1,r,l));//最近距离        printf("%.5lf\n",mindis);    }    return 0;}