POJ 3525 Most Distant Point from the Sea

这篇博客介绍了POJ 3525题目,讨论如何求解凸多边形内部的点到所有边的最小距离最大值。作者提出使用二分查找配合边的旋转技巧,结合半平面交来解决这一计算几何问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意 :给你一个小岛,是一个凸多边形,问你这个多边形的内部的点到所有边的最小值最大。

题解 :对于最小值最大的问题,不难想到要用二分去做,不难发现这个题的距离是满足二分性质的,然后将每天边向内部缩 dist 怎么缩呢 ? (通过一天边旋转90 度 就可以了) 。由于这个题给你的是一个凸多边形所以剩下的就可以用半平面交解决了,如果半平面交存在,那么这个答案检验就是可以的。

emmm……. 剩下的就是半平面交的模版了。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define PI 3.1415926
using namespace std;
#define N 1005
const double eps=1e-8;
int dcmp(double x) {
    if (x<=eps&&x>=-eps) return 0;
    return (x>0)?1:-1;
}
struct Vector {
    double x,y;
    Vector(double X=0,double Y=0){
        x=X,y=Y;
    }
};
typedef Vector Point;

struct Line {
    Point p;
    Vector v;
    double ang;
    Line(Point P=Point(0,0),Vector V=Vector(0,0)) {
        p=P,v=V;
        ang=atan2(v.y,v.x);
    }
    bool operator < (const Line &a) const {
        return ang<a.ang;
    }
};
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 b) {return Vector(a.x*b,a.y*b);}

int n,l,r,cnt;
double ans;
Line L[N],q[N];
Point p[N],poly[N];
double Cross(Vector a,Vector b) {
    return a.x*b.y-a.y*b.x;
}
Point GLI(Point P,Vector v,Point Q,Vector w) {
    Vector u=P-Q;
    double t=Cross(w,u)/Cross(v,w);
    return P+v*t;
}
bool Onleft(Line m,Point P) {
    Vector w=P-m.p;
    return dcmp(Cross(m.v,w))>=0;
}
void halfp(){
    sort(L+1,L+n+1);
    cnt=0;
    q[l=r=1]=L[1];
    for (int i=2;i<=n;++i) {
        while (l<r&&!Onleft(L[i],p[r-1])) --r;
        while (l<r&&!Onleft(L[i],p[l])) ++l;
        q[++r]=L[i];
        if (dcmp(Cross(q[r].v,q[r-1].v))==0) {
            --r;
            if (Onleft(q[r],L[i].p))
                q[r]=L[i];
        }
        if (l<r)
            p[r-1]=GLI(q[r-1].p,q[r-1].v,q[r].p,q[r].v);
    }
    while (l<r&&!Onleft(q[l],p[r-1]))
        --r;
    if (r-l<=1) return;
    p[r]=GLI(q[r].p,q[r].v,q[l].p,q[l].v);
    for (int i=l;i<=r;++i) poly[++cnt]=p[i];
}
double Area() {
    double ans=0;
    for (int i=2;i<cnt;++i)
        ans+=Cross(poly[i]-poly[1],poly[i+1]-poly[1]);
    return fabs(ans/2);
}

int main() {
    while(scanf("%d",&n) && n) { // 输入点的个数
    Point temp[N],temp0,temp1;
    Point pp[N];
    Line LL[N];
    double ang[N];
    for (int i=1;i<=n;++i) {
        double x,y;
        scanf ("%lf%lf",&x,&y);
        pp[i] = Point (x,y);
        temp[i] = pp[i];
    } // 开始需要设置一个无限大的区域
    pp[n + 1] = pp[1];
    temp[n + 1] = pp[1];
    for (int i = 1;i <= n; ++ i) {
        L[i] = Line(pp[i],pp[i + 1] - pp[i]);
        LL[i] = L[i];
        ang[i] = L[i].ang;
    }
//    for (int i = 1;i <= n; ++ i) printf("%d  %.6f\n",i,LL[i].ang);
    L[++n] = Line(Point(0,10000),Vector(0,-10000));
    L[++n] = Line(Point(0,0),Vector(10000,0));
    L[++n] = Line(Point(10000,0),Vector(0,10000));
    L[++n] = Line(Point(10000,10000),Vector(-10000,0));
    double R = 10000.0,Ll = 0.0;
    double mid = (R + Ll) / 2;
//    halfp();
//    printf("%.6f\n",Area());
//    cout << cnt << endl;
//    for (int i = 1;i <= n - 4; ++ i) printf("%d  %.6f\n",i,LL[i].ang);
    while (R - Ll > eps) {
        mid = (R + Ll) / 2.0;;
        for (int i = 1;i <= n - 4; ++ i) {
            double angd = ang[i] + PI / 2.0;
            double addx = mid * cos (angd);
            double addy = mid * sin (angd);
            temp0 = temp[i];
            temp1 = temp[i + 1];
            temp0.x += addx,temp0.y += addy;
            temp1.x += addx,temp1.y += addy;
            L[i] = Line (temp0,temp1 - temp0);
        //   printf("%.3f %.3f %.3f\n",mid,L[i].p.x,L[i].p.y);
        }
        L[n] = Line(Point(0,10000),Vector(0,-10000));
        L[n - 1] = Line(Point(0,0),Vector(10000,0));
        L[n - 2] = Line(Point(10000,0),Vector(0,10000));
        L[n - 3] = Line(Point(10000,10000),Vector(-10000,0));
        halfp();
        if (cnt >= 1) Ll = mid;
        else R = mid;
//        printf ("%.6f\n",mid);
    }
    printf ("%.6f\n",mid);
    }
    return 0;
}
/*
 3
 0.000 312.500
 9901.179 -296.464
 7044.194 690.641
 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值