题意:给出两个点集p和q,求这两个点集之间的最小距离,如果p与q相交,或者p包含q,或者q包含p,则输出0.0000。
思路:先对两个点集各求一次凸包得到s,t两个凸包,用得到的凸包先判断两个凸包是否相交,分别枚举凸包t的每条边是否与s中的边相交,s中的每条边是否与t中的每条边相交,如果相交则输出0.0000,否则再判断两个凸包是否有包含关系,枚举s上的点是否在t内,如果有就跳出输出0.0000,没有的话再枚举t上的点是否在s内,如果有就跳出输出0.0000,没有的话就对两个凸包进行旋转卡壳求两个凸包的最小距离。
#include<iostream>
#include<algorithm>
#include<string.h>
#include<stack>
#include<queue>
#include<math.h>
#include<cstdio>
using namespace std;
const double PI = acos(-1.0);
const double MAX=1e9;
const double eps=1e-9;
struct point
{
double x,y;
point () {}
point (double xx,double yy): x(xx),y(yy) {}
}p1[10010],p2[10010],res1[10010],res2[10010];
struct Segment
{
point a,b;
Segment(){}
Segment(point a,point b):a(a),b(b) {}
};
point operator + (point a,point b) { return point(a.x+b.x,a.y+b.y); }
point operator - (point a,point b) { return point(a.x-b.x,a.y-b.y); }
point operator * (point a,point b) { return point(a.x*b.x,a.y*b.y); }
point operator / (point a,point b) { return point(a.x/b.x,a.y/b.y); }
double cross(point a,point b,point op)
{
return (op.x-a.x)*(op.y-b.y)-(op.y-a.y)*(op.x-b.x);
}
double dot(point a,point b)
{
return a.x*b.x+a.y*b.y;
}
double dist_p_to_p(point a,point b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
bool inter(point a,point b,point c,point d)
{
if( max(a.x,b.x)>=min(c.x,d.x) &&
max(c.x,d.x)>=min(a.x,b.x) &&
max(a.y,b.y)>=min(c.y,d.y) &&
max(c.y,d.y)>=min(a.y,b.y) &&
cross(a,b,c)*cross(a,b,d)<=0 &&
cross(c,d,a)*cross(c,d,b)<=0 )
return true;
else return false;
}
double dist_p_to_seg(point c,point a,point b)
{
point ab=b-a;
point ac=c-a;
double f=dot(ab,ac);
if(f<0) return dist_p_to_p(a,c);
double D=dot(ab,ab);
if(f>D) return dist_p_to_p(b,c);
f=f/D;
point d=point(a.x+f*ab.x,a.y+f*ab.y);
return dist_p_to_p(d,c);
}
double dist_seg_to_seg(point p1, point p2, point p3, point p4)
{
return min(min(dist_p_to_seg(p1, p3, p4), dist_p_to_seg(p2, p3, p4)),
min(dist_p_to_seg(p3, p1, p2), dist_p_to_seg(p4, p1, p2)));
}
bool mult(point sp, point ep, point op)
{
return (sp.x - op.x) * (ep.y - op.y)
>= (ep.x - op.x) * (sp.y - op.y);
}
bool operator < (const point &l, const point &r)
{
return l.y < r.y || (l.y == r.y && l.x < r.x);
}
int graham(point pnt[], int n, point res[])
{
int i, len, k = 0, top = 1;
sort(pnt, pnt + n);
if (n == 0) return 0; res[0] = pnt[0];
if (n == 1) return 1; res[1] = pnt[1];
if (n == 2) return 2; res[2] = pnt[2];
for (i = 2; i < n; i++)
{
while (top && mult(pnt[i], res[top], res[top-1]))
top--;
res[++top] = pnt[i];
}
len = top; res[++top] = pnt[n - 2];
for (i = n - 3; i >= 0; i--)
{
while (top!=len && mult(pnt[i], res[top],
res[top-1])) top--;
res[++top] = pnt[i];
}
return top;
}
double rc(point pp[],point qq[],int n,int m)
{
int q=0;
int p=0;
for(int i=0;i<n;i++)
if(pp[i].y-pp[p].y<-eps)
p=i;
for(int i=0;i<m;i++)
if(qq[i].y-qq[q].y>eps)
q=i;
pp[n]=pp[0];
qq[m]=qq[0];
double tmp,ans=1e99;
for(int i=0;i<n;i++)
{
while((tmp=cross(pp[p+1],qq[q+1],pp[p])-cross(pp[p+1],qq[q],pp[p]))>eps)
q=(q+1)%m;
if(tmp<-eps)
ans=min(ans,dist_p_to_seg(qq[q],pp[p],pp[p+1]));
else
ans=min(ans,dist_seg_to_seg(pp[p],pp[p+1],qq[q],qq[q+1]));
p=(p+1)%n;
}
return ans;
}
bool is_online(point p, Segment s)
{
if(fabs(cross(s.a, p, s.b)) <= eps && min(s.a.x, s.b.x) <= p.x &&
p.x <= max(s.a.x, s.b.x) && min(s.a.y, s.b.y) <= p.y && p.y <= max(s.a.y, s.b.y))
return true;
return false;
}
bool is_segment_crossing(Segment u, Segment v)
{
return((max(u.a.x, u.b.x) >= min(v.a.x, v.b.x)) &&
(max(v.a.x, v.b.x) >= min(u.a.x, u.b.x)) &&
(max(u.a.y, u.b.y) >= min(v.a.y, v.b.y)) &&
(max(v.a.y, v.b.y) >= min(u.a.y, u.b.y)) &&
(cross(v.a, u.b, u.a) * cross(u.b, v.b, u.a) >= 0) &&
(cross(u.a, v.b, v.a) * cross(v.b, u.b, v.a) >= 0));
}
bool inPolygon(point p, point s[], int len) /*判断点p是否在以边点顺序排列的点集s所围成的多边形内*/
{
Segment seg;
Segment pp;
int num = 0;
point MM;/*定义无穷点*/
MM.y = p.y;
MM.x = MAX;
pp.a = p;
pp.b = MM; /*构造一条平行于x轴的以p为端点的右射线pp*/
for(int i = 0; i < len; i++)
{
seg.a = s[i];
seg.b = s[(i+1)%len];
if(is_online(p, seg)) /*点p在边上*/
return true;
if(fabs(seg.a.y - seg.b.y) < eps) /*水平*/
continue;
if(is_online(seg.a, pp)) /*边的一端点在射线上*/
{
if(seg.a.y > seg.b.y)
num++;
}
else if(is_online(seg.b, pp))
{
if(seg.b.y > seg.a.y)
num++;
}
else if(is_segment_crossing(pp, seg))
num++;
}
if(num % 2 == 1)
return true;
return false;
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
for(int i=0;i<n;i++)
scanf("%lf%lf",&p1[i].x,&p1[i].y);
for(int i=0;i<m;i++)
scanf("%lf%lf",&p2[i].x,&p2[i].y);
if(n==1 && m==1)
{
printf("%.4lf\n",dist_p_to_p(p1[0],p2[0]));
continue;
}
int cnt1=graham(p1,n,res1);
int cnt2=graham(p2,m,res2);
res1[cnt1]=res1[0];
res2[cnt2]=res2[0];
bool flag=0;
for(int i=0;i<cnt1;i++)
{
for(int j=0;j<cnt2;j++)
{
if(inter(res1[i],res1[i+1],res2[j],res2[j+1]))
{
flag=1;
break;
}
}
if(flag) break;
}
if(!flag)
{
bool f=0;
for(int i=0;i<cnt1;i++)
if(inPolygon(res1[i],res2,cnt2))
{
f=1;
break;
}
if(f)
{
puts("0.0000");
continue;
}
for(int i=0;i<cnt2;i++)
if(inPolygon(res2[i],res1,cnt1))
{
f=1;
break;
}
if(f)
{
puts("0.0000");
continue;
}
double ans=min(rc(res1,res2,cnt1,cnt2),rc(res2,res1,cnt2,cnt1));
printf("%.4lf\n",ans);
}
else puts("0.0000");
}
return 0;
}
/*
3 3
0 0
9 0
0 9
1 1
1 5
5 1
3 3
0 0
0 9
9 0
9 0
0 9
10 10
3 3
0 0
9 0
0 9
5 0
10 0
10 10
*/