题目地址:http://poj.org/problem?id=3608
题目的意思已经说的很清楚了,就是要你求出给出的两个凸包的最短距离,但是这两个凸包的点给的并不规律,所以你得自己重新建立凸包。这题一拿着一点思路都没有,查了一下午的资料,看了一些大牛的博客,终于搞清楚了。用所谓的旋转卡壳法。很美妙的思路。
别人已经写得十分的好了,所以我根本没必要自己再转述一遍。
理论请移步:http://blog.youkuaiyun.com/dr5459/article/details/7802055
但是有几点要注意:
1、凸包要建好,这是一切的基础。
2、凸包最好是用犄角排序,不知道为什么我用别的排序是错的。
3、要考虑到两个凸包最近的三种情况,所以要考虑仔细,这一点在我的代码里面体现的很明显。
4、自己好烂,要多加学习。
下面上代码:
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
#define eps 1e-8
#define MAXN 10010
double min(double a,double b)
{
return a<b?a:b;
}
struct point
{
double x,y;
};
struct convex
{
point p[MAXN];
int num_of_points;
};
convex c1;
convex c2;
point p[MAXN];
inline double dis_points(point a,point b)
{
return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}
inline double multiply(point p0,point p1,point p2)
{
return (p1.x - p0.x)*(p2.y - p1.y) - (p2.x - p1.x)*(p1.y - p0.y);
}
inline double fabss(double x)
{
return x>0?x:-x;
}
bool cmp1(point a,point b) //进行犄角排序
{
double m = multiply(p[0],a,b);
if(m > eps)
return true;
if(fabss(m) <= eps)
return dis_points(p[0],a) < dis_points(p[0],b);
return false;
}
inline void init_convex(point from[],convex &to,int num)
{
int u=0;
int top = 0;
int i;
for(i=1;i<num;i++)
{
if(from[u].y > from[i].y || (from[u].y == from[i].y && from[u].x > from[i].x))
u = i;
}
//cout<<"num "<<num<<endl;
swap(from[u],from[0]);
sort(from+1,from+num,cmp1);
for(i=0;i<=1;i++)
to.p[i] = from[i];
top = 1;
for(i=2;i<num;i++)
{
while(top>=0 && multiply(to.p[top-1],to.p[top],from[i]) < 0)
top --;
to.p[++top] = from[i];
}
top++;
to.p[top] = from[num-2];
for(i=num-3;i>=0;i--)
{
while(top>=0 && multiply(to.p[top-1],to.p[top],from[i]) < 0)
top --;
to.p[++top] = from[i];
}
//cout<<"top "<<top<<endl;
to.num_of_points = top;
}
//点积公式,a为起点
inline double dot_time(point a,point b,point c)
{
return (b.x - a.x)*(c.x-a.x) + (b.y-a.y)*(c.y-a.y);
}
inline double dis_point_to_line(point a,point b,point c)
{
//这里巧妙的利用了叉积公式,用面积来转化距离
return fabs(multiply(c,a,b) / dis_points(a,b));
}
//点c与线段a,b的距离
inline double dis_point_to_seg(point a,point b,point c)
{
double ans;
//如果以点c做直线a,b的垂线不能与线段a,b相交,则只能取
//ac,bc最短的,否则就是c到线段a,b的距离
//我们用点积来判断以c做垂线到ab能否相交
if(dot_time(a,b,c) * dot_time(b,a,c) < 0)
{
ans = min(dis_points(a,c), dis_points(b,c));
}
else
//求点c到直线ab的距离
ans = dis_point_to_line(a,b,c);
return ans;
}
inline double min_dis_convexs(convex c1,convex c2)
{
double ans;
int n1 = 0,n2 = 0;
int i;
//找出两个凸包中最低的点和最高的点
//在第一个凸包里面找最低的,在第二个里面找最高的
for(i=1;i<c1.num_of_points;i++)
{
if(c1.p[n1].y > c1.p[i].y)
n1 = i;
}
for(i=1;i<c2.num_of_points;i++)
{
if(c2.p[n2].y < c2.p[i].y)
n2 = i;
}
int t1 = n1,t2 = n2;
bool f1 = true, f2 = true;//表示两个凸包旋转是否转完
//true则表示还没有转完
point a,b,o;
ans = dis_points(c1.p[n1],c2.p[n2]); //先求出两个
//最初的点的距离,然后在后面进行更新
do
{
//构造向量a
a = c1.p[(n1+1)%c1.num_of_points];
a.x -= c1.p[n1].x;
a.y -= c1.p[n1].y;
//构造向量b
b = c2.p[n2];
b.x -= c2.p[(n2 + 1)% c2.num_of_points].x;
b.y -= c2.p[(n2 + 1)% c2.num_of_points].y;
o.x = 0;
o.y = 0;
double rot = multiply(o,a,b);
if(rot < 0) //转n2最快达到平行
{
ans = min(ans,dis_point_to_seg(c2.p[n2],c2.p[(n2+1)%c2.num_of_points],c1.p[n1]));
n2 = (n2+1)%c2.num_of_points;
if(n2 == t2)
f2 = false;
}
else if(rot > 0)//转n1最快达到平行
{
ans = min(ans,dis_point_to_seg(c1.p[n1],c1.p[(n1+1)%c1.num_of_points],c2.p[n2]));
n1 = (n1+1)%c1.num_of_points;
if(n1 == t1)
f1 = false;
}
else //如果他们同时转达到平行
//最短距离可能就会出现在四种情况,详见代码
{
ans = min(ans,dis_point_to_seg(c1.p[n1],c1.p[(n1+1)%c1.num_of_points],c2.p[n2]));
ans = min(ans,dis_point_to_seg(c1.p[n1],c1.p[(n1+1)%c1.num_of_points],c2.p[(n2+1)%c2.num_of_points]));
ans = min(ans,dis_point_to_seg(c2.p[n2],c2.p[(n2+1)%c2.num_of_points],c1.p[n1]));
ans = min(ans,dis_point_to_seg(c2.p[n2],c2.p[(n2+1)%c2.num_of_points],c1.p[(n1+1)%c1.num_of_points]));
n1 = (n1+1)%c1.num_of_points;
n2 = (n2+1)%c2.num_of_points;
if(n1 == t1)
f1 = false;
if(n2 == t2)
f2 = false;
}
}while(f1 || f2);
return ans;
}
int main()
{
int n1,n2;
while(scanf("%d%d",&n1,&n2) != EOF && (n1 || n2))
{
int i;
for(i=0;i<n1;i++)
scanf("%lf%lf",&p[i].x,&p[i].y);
init_convex(p,c1,n1);
//for(i=0;i<c1.num_of_points;i++)
//cout<<"c1 "<<c1.p[i].x<<c1.p[i].y<<endl;
for(i=0;i<n2;i++)
scanf("%lf%lf",&p[i].x,&p[i].y);
init_convex(p,c2,n2);
double ans = min_dis_convexs(c1,c2);
printf("%.5lf\n",ans+eps);
}
return 0;
}