题目链接:http://poj.org/problem?id=3608
表示旋转卡壳这个算法看起来比较好懂(虽然证明起来还是有一点难度的,但是只观的看还是比较好理解的)但是代码实现起来还是不怎么好理解的
关键在计算几何这里面要求尽量不涉及到角度的计算,基本上都是点积和差积,因为这样能避免调用系统的三角函数,所以能最大限度的减少精度
损失
这个题目可以用枚举方法,可以用凸包的单调性来剪枝,但是还是容易超时
但是旋转卡壳算法O(n)的复杂度,应该来说在渐进时间复杂度的意义上是最优的算法了
首先选取p的最低点,和q的最高点,完成之后就开始卡壳,卡壳的时候还是利用面积
while((rec=cross(q[pos_q],p[pos_p+1],p[pos_p])-cross(q[pos_q+1],p[pos_p+1],p[pos_p])) > eps)
{
pos_q=(pos_q+1)%m;
}
if(rec < -eps)
ans=MIN(ans,point_to_seg(p[pos_p],p[pos_p+1],q[pos_q]));
else
ans=MIN(ans,seg_to_seg(p[pos_p],p[pos_p+1],q[pos_q],q[pos_q+1]));
pos_p=(pos_p+1)%n;
如果这次的面积比下一次的大,那么说明再往下走有可能更小,也就是还没找到最近的点,接着向下
不过还有一个条件,那么就是如果两次面积差接近0,那么说明两次的面积没有什么变化,这个最短
距离就是ans和两条线段之间的最短距离了,如果两次距离差不是0,那么就是点到线段之间的距离了
这个还是比较好理解的,然后一次做完之后p就到下一个点开始做,可能有人认为这个算法是一个平方
算法,其实还是线性的,因为里面的循环只是在第一次的时候可能会转半圈,后来就几乎是常数了!
至于最后结果为什么要两边都枚举一遍,一个取最低点,一个取最高点,然后反过来一下!
旋转卡壳算法还没怎么理解,还要好好学习!
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <cmath>
#include <algorithm>
using namespace std;
#define inf 1e8
#define eps 1e-8
#define MAX(a,b) (a>b?a:b)
#define MIN(a,b) (a<b?a:b)
struct point
{
double x;
double y;
}p[11000],q[11000];
struct line
{
point a;
point b;
};
double cross(point &a,point &b,point &c)
{
return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
double dis(point &a,point &b)
{
return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y);
}
int cmp_p(const void *a,const void *b)
{
return cross(p[0],*(point*)a,*(point*)b) > 0 ? -1 : 1;
}
int cmp_q(const void *a,const void *b)
{
return cross(q[0],*(point*)a,*(point*)b) > 0 ? -1 : 1;
}
double dot(point a,point b)
{
return a.x*b.x+a.y*b.y;
}
double point_to_seg(point a,point b,point c)//c 到直线a-b的距离
{
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)));
}
int n,m;
double find_ans(point *p,int n,point *q,int m)
{
int i,j,k,pos_p,pos_q;
double ans=inf,rec;
p[n]=p[0];
q[m]=q[0];
pos_p=pos_q=0;
for(i=1;i<n;i++)
if(p[i].y < p[pos_p].y)
pos_p=i;
for(i=1;i<m;i++)
if(q[i].y > q[pos_q].y)
pos_q=i;
for(i=0;i<n;i++)
{
while((rec=cross(q[pos_q],p[pos_p+1],p[pos_p])-cross(q[pos_q+1],p[pos_p+1],p[pos_p])) > eps)
{
pos_q=(pos_q+1)%m;
}
if(rec < -eps)
ans=MIN(ans,point_to_seg(p[pos_p],p[pos_p+1],q[pos_q]));
else
ans=MIN(ans,seg_to_seg(p[pos_p],p[pos_p+1],q[pos_q],q[pos_q+1]));
pos_p=(pos_p+1)%n;
}
return ans;
}
int main()
{
int i,j,k,pos;
point temp,temp1;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==0 && m==0)
return 0;
temp.y=1e8;
for(i=0;i<n;i++)
{
scanf("%lf%lf",&p[i].x,&p[i].y);
if(p[i].y < temp.y)
temp=p[i],pos=i;
}
p[pos]=p[0],p[0]=temp;
temp.y=1e8;
for(i=0;i<m;i++)
{
scanf("%lf%lf",&q[i].x,&q[i].y);
if(q[i].y < temp.y)
temp=q[i],pos=i;
}
q[pos]=q[0],q[0]=temp;
qsort(p,n,sizeof(p[0]),cmp_p);
qsort(q,m,sizeof(q[0]),cmp_q);
printf("%.5lf\n",sqrt(MIN(find_ans(p,n,q,m),find_ans(q,m,p,n))));
}
return 0;
}