poj3608_Bridge Across Islands_旋转卡壳&&点到线段的距离

题意

给出两个凸包,现要用一条线段连接这两个凸包,求这条线段的最小长度。

思路

从第一个凸包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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值