Time Limit: 1000MS | Memory Limit: 65536K | |||
Total Submissions: 9843 | Accepted: 2886 | Special Judge |
Description
Thousands of thousands years ago there was a small kingdom located in the middle of the Pacific Ocean. The territory of the kingdom consists two separated islands. Due to the impact of the ocean current, the shapes of both the islands became convex polygons. The king of the kingdom wanted to establish a bridge to connect the two islands. To minimize the cost, the king asked you, the bishop, to find the minimal distance between the boundaries of the two islands.
Input
The input consists of several test cases.
Each test case begins with two integers N, M. (3 ≤ N, M ≤ 10000)
Each of the next N lines contains a pair of coordinates, which describes the position of a vertex in one convex polygon.
Each of the next M lines contains a pair of coordinates, which describes the position of a vertex in the other convex polygon.
A line with N = M = 0 indicates the end of input.
The coordinates are within the range [-10000, 10000].
Output
For each test case output the minimal distance. An error within 0.001 is acceptable.
解题思路:
用旋转卡壳算法求凸包间最短距离:
两个凸多边形 P 和Q 之间的最小距离由多边形间的对踵点对确立。 存在凸多边形间的三种多边形间的对踵点对,因此就有三种可能存在的最小距离模式:
在P上找出最下方的点记作C,在Q上找最上方的点记作D,记C在逆时针方向的下一个顶点是A,同理定义B。那么我们得到了两个线段AC和DB,分别逆时针同时逐步旋转两线段,那么最短距离肯定在这两条线段上产生。
具体实现时,同时旋转体现为逐步选取逆时针方向上的下一个顶点作为C或D、A或B;两条线段的最短距离归结于四个顶点到另一线段的最短距离或直线的最短距离(平行),而点到线段的距离又归结于点到两个端点和点到直线(如果角度满足的话)的垂线距离,可见不平行的时候可以少考虑一种情况。
至于如何判断平行,代码中实际在用向量AC和DB的叉积判断两者是否平行。
代码:(套模板的)#include
#include
#include
#define N 10005
#define PI 3.14159265358979
#define EPS 1e-7
using namespace std;
struct PO
{
double x,y;
}p1[N],p2[N],o;
int n,m,top1,top2,stk1[N],stk2[N];
inline int doublecmp(double x)//误差修正
{
if(x>EPS) return 1;
else if(x<-EPS) return -1;
return 0;
}
inline bool cmp(const PO &a,const PO &b)//点排序
{
if(doublecmp(a.x-b.x)==0) return a.y=1&&doublecmp(cross(p[stk[top-1]],p[stk[top]],p[i]))<=0) top--;
stk[++top]=i;
}
int tmp=top;
for(int i=gs-1;i>=1;i--)
{
while(top>=tmp+1&&doublecmp(cross(p[stk[top-1]],p[stk[top]],p[i]))<=0) top--;
stk[++top]=i;
}
}
inline double rotating_calipers()//旋转卡壳
{
int s1=0,s2=0;
for(int i=1;i0||(doublecmp(p2[stk2[i]].y-p2[stk2[s2]].y)==0)&&doublecmp(p2[stk2[i]].x-p2[stk2[s2]].x)>0)//找到纵坐标最大的点,纵坐标相同取横坐标较小的;
s2=i;
int t1=s1,t2=s2;
double ans=getdis(p1[stk1[s1]],p2[stk2[s2]]);
do
{
double af=getangle(p1[stk1[s1]],p1[stk1[(s1+1)%top1]],p2[stk2[s2]],p2[stk2[(s2+1)%top2]]);
if(doublecmp(af)==0)//卡壳到两条边
{
ans=min(ans,getdis_ps(p1[stk1[s1]],p2[stk2[s2]],p2[stk2[(s2+1)%top2]]));
ans=min(ans,getdis_ps(p1[stk1[(s1+1)%top1]],p2[stk2[s2]],p2[stk2[(s2+1)%top2]]));
ans=min(ans,getdis_ps(p2[stk2[s2]],p1[stk1[s1]],p1[stk1[(s1+1)%top1]]));
ans=min(ans,getdis_ps(p2[stk2[(s2+1)%top2]],p1[stk1[s1]],p1[stk1[(s1+1)%top1]]));
s1=(s1+1)%top1; s2=(s2+1)%top2;
}
else if(doublecmp(af)>0)//卡壳到第一个的边,第二个的点
{
ans=min(ans,getdis_ps(p2[stk2[s2]],p1[stk1[s1]],p1[stk1[(s1+1)%top1]]));
s1=(s1+1)%top1;
}
else//卡壳到第二个的边,第一个的点
{
ans=min(ans,getdis_ps(p1[stk1[s1]],p2[stk2[s2]],p2[stk2[(s2+1)%top2]]));
s2=(s2+1)%top2;
}
}while(t1!=s1||t2!=s2);
return ans;
}
inline void go()
{
graham(p1,stk1,top1,n);
graham(p2,stk2,top2,m);
printf("%.5f\n",rotating_calipers());
}
int main()
{
while(scanf("%d%d",&n,&m),n)
read(),go();
return 0;
}