题意
给出两个凸包,现要用一条线段连接这两个凸包,求这条线段的最小长度。
思路
从第一个凸包y值最小的那个点,和第二个凸包y值最大的那个点出发,旋转卡壳。
链接
http://poj.org/problem?id=3608
代码
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const double eps = 1e-8;
const int maxn = 10101;
const double inf = 1e11;
struct P{
double x, y;
P(){}
P(double x, double y): x(x), y(y){}
void read(){
scanf("%lf %lf", &x, &y);
}
P operator -(P p){
return P(x - p.x, y - p.y);
}
P operator *(double p){
return P(x * p, y * p);
}
double dot(P p){
return x * p.x + y * p.y;
}
double det(P p){
return x * p.y - y * p.x;
}
};
int n, m;
P A[maxn], B[maxn];
//将两个凸包分别排成逆时针顺序
void AntiClockWise(P *A, int n){
for(int i = 0; i < n - 2; ++i){
if((A[i+1] - A[i]).det(A[i+2] - A[i]) > eps) return;
else if((A[i+1] - A[i]).det(A[i+2] - A[i]) < -eps){
reverse(A, A + n);
return;
}
}
}
//计算 a b 两点之间的距离
double dist(P a, P b){
double x = a.x - b.x, y = a.y - b.y;
return sqrt(x * x + y * y);
}
//计算点 a 到线段 bc 的距离
double distPL(P a, P b, P c){
if(dist(b, c) < eps) return dist(a, c);
if((a - b).dot(c - b) < -eps) return dist(a, b);
if((a - c).dot(b - c) < -eps) return dist(a, c);
return fabs((a - c).det(b - c) / dist(b, c));
}
//计算线段 ab 和 cd 之间的最小距离
double distLL(P a, P b, P c, P d){
return min(min(distPL(c, a, b), distPL(d, a, b)), min(distPL(a, c, d), distPL(b, c, d)));
}
double solve(){
int it0 = 0, it1 = 0;
for(int i = 0; i < n; ++i){//找到第一个凸包y值最小的点
if(A[i].y < A[it0].y) it0 = i;
}
for(int i = 0; i < m; ++i){//找到第二个凸包y值最大的点
if(B[i].y > B[it1].y) it1 = i;
}
A[n] = A[0], B[m] = B[0];//将凸包首尾相接,方便旋转
double res = inf;
for(int i = 0; i < n; ++i){
while((B[it1] - A[it0]).det(A[it0+1] - A[it0]) - (B[it1 + 1] - A[it0]).det(A[it0+1] - A[it0]) > eps)
it1 = (it1 + 1) % m;
res = min(res, distLL(A[it0], A[it0+1], B[it1], B[it1+1]));
it0 = (it0 + 1) % n;
}
return res;
}
int main(){
while(scanf("%d %d", &n, &m) == 2){
if(n == 0 && m == 0) break;
for(int i = 0; i < n; ++i){
A[i].read();
}
for(int i = 0; i < m; ++i){
B[i].read();
}
AntiClockWise(A, n);
AntiClockWise(B, m);
printf("%.5f\n", solve());
}
return 0;
}