凸包最近距离 旋转卡壳

original link - http://poj.org/problem?id=3608

题意:

给出两个顺时针排序好的凸包,求这两个凸包的最近点对距离。

解析:

旋转卡壳的另外一个应用。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
先找出P1P1P1中的最低点,P2P2P2中的最高点,然后旋转(顺时针逆时针随意,要保证两个切线旋转方向相同)。

每次P1P1P1的切线旋转到与一条边重合,找出P2P2P2中离这条切线最近的点

1. 如果只有一个点,那么就是点到线的距离
2. 否则就是两条边4个点的最近距离

显然这样会漏掉情况,所以要对P2P2P2也做一遍。

代码:

#include<stdio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<time.h>
using namespace std;
const int maxn = 5e4 + 9;
const double eps = 1e-7;
struct point {
    double x, y;
    point(double x = 0, double y = 0): x(x), y(y) {}
};
typedef point P;
typedef point Point;
typedef point Vector;
Vector operator + (Vector a, Vector b) { //向量加法
    return Vector(a.x + b.x, a.y + b.y);
}
Vector operator - (Vector a, Vector b) { //向量减法
    return Vector(a.x - b.x, a.y - b.y);
}
Vector operator * (Vector a, double p) { //向量数乘
    return Vector(a.x * p, a.y * p);
}
Vector operator / (Vector a, double p) { //向量数除
    return Vector(a.x / p, a.y / p);
}
int dcmp(double x) { //精度三态函数(>0,<0,=0)
    if (fabs(x) < eps)
        return 0;
    else if (x > 0)
        return 1;
    return -1;
}
bool operator == (const Point &a, const Point &b) { //向量相等
    return dcmp(a.x - b.x) == 0 && dcmp(a.y - b.y) == 0;
}
double Cross(P a, P b) { // 叉积 平行四边形面积
    return (a.x * b.y) - (b.x * a.y);
}
double Dot(P a, P b) {
    return a.x * b.x + a.y * b.y;
}
double Length(Vector v) {
    return sqrt(v.x * v.x + v.y * v.y);
}
double Distance(P a, P b) {
    return sqrt(Length(a - b));
}

point p1[maxn], p2[maxn];
int n1, n2;

#define rep(i,a,b) for(int i=a;i<=b;i++)
double DistanceToSeg(Point P, Point A, Point B) {
    if(A == B)
        return Length(P - A);
    Vector v1 = B - A, v2 = P - A, v3 = P - B;
    if(dcmp(Dot(v1, v2)) < 0)
        return Length(v2);
    if(dcmp(Dot(v1, v3)) > 0)
        return Length(v3);
    return fabs(Cross(v1, v2)) / Length(v1);
}
double SegDistancetoSeg(Point A, Point B, Point C, Point D) {
    return min(min(DistanceToSeg(C, A, B), DistanceToSeg(D, A, B)), 
    	min(DistanceToSeg(A, C, D), DistanceToSeg(B, C, D)));
}

double RotateCalipers(P *p1, int n1, P *p2, int n2) {
    int q1 = 1, q2 = 1; // q1 ymin, q2 ymax
    rep(i, 2, n1)
        if(p1[i].y < p1[q1].y)
            q1 = i;
    rep(i, 2, n2)
        if(p2[i].y > p2[q2].y)
            q2 = i;
    double ans = 1e18, tmp;
    p1[n1 + 1] = p1[1], p2[n2 + 1] = p2[1];
    rep(i, 1, n1) {
        while(dcmp(tmp = Cross(p1[q1 + 1] - p1[q1], p2[q2 + 1] - p1[q1]) -
                         Cross(p1[q1 + 1] - p1[q1], p2[q2] - p1[q1])) > 0)
            q2 = q2 % n2 + 1;
        if(dcmp(tmp) < 0)
            ans = min(ans, 
            	DistanceToSeg(p2[q2], p1[q1], p1[q1 + 1]));
        else
            ans = min(ans, 
            	SegDistancetoSeg(p1[q1], p1[q1 + 1], p2[q2], p2[q2 + 1]));
        q1 = q1 % n1 + 1;
    }
    return ans;
}

int main() {
    while(scanf("%d%d", &n1, &n2), n1 + n2) {
        rep(i, 1, n1)
        scanf("%lf%lf", &p1[i].x, &p1[i].y);
        rep(i, 1, n2)
        scanf("%lf%lf", &p2[i].x, &p2[i].y);
        printf("%.5f\n", min(RotateCalipers(p1, n1, p2, n2), 
        	RotateCalipers(p2, n2, p1, n1)));
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值