Gym 101464C - 计算几何+二分(uva1463)

Posted lqerio

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Gym 101464C - 计算几何+二分(uva1463)相关的知识,希望对你有一定的参考价值。

网络赛的A题。不是很难,但是我觉得对代码能力的要求还是挺高的。

注意模块化。

因为是浮点数,所以二分用的很多很多。

参考 https://blog.csdn.net/njupt_lyy/article/details/81256538?utm_source=blogxgwz4

对半径二分,这样我们只需要判断能不能放的下这个圆。这时,通过给定的半径,对于每一条线段可以找到一个区间(或者为空),使得圆心不能落在这个区间上,我们只需要判断区间的并集是否覆盖了[0,L]。那么如何去找到这个区间呢?对于每一个线段,我们可以找到线段上y坐标的绝对值最小的点,这个点一定是线段的端点或者是零点,这是线段到直线的最短距离。如果最短距离小于半径,那么区间为空;如果最短距离大于半径,这个点两边的点到线段都具有单调性,我们对左右两侧分别二分找到距离等于半径的点即可。

细节见注释:

技术图片

 

 

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define eps 1e-6

struct point

    double x,y;
    point()
    point(double _x,double _y)
    
        x = _x;y = _y;
    
    point operator -(const point &b)const
    
        return point(x - b.x,y - b.y);
    ;
    double operator ^(const point &b)const   //当线段过x轴时,用于求与x轴交点的x坐标
    
        return x*b.y - y*b.x;
    
    double operator *(const point &b)const
    
        return x*b.x + y*b.y;
    
;


struct line               //线段。e点在右

    point s,e;
c[2005];


int t,n,L;
struct st

    double l,r;
;
vector<st> v;

double dist(point a,point b)                //两点距离

    return sqrt((a-b)*(a-b));


point NearestPointToLineSeg(point P,line L)                   //返回线段L上距离P最近的点  也是相似三角形。

    point result;
    double t = ((P-L.s)*(L.e-L.s))/((L.e-L.s)*(L.e-L.s));
    if(t >= 0 && t <= 1)
    
        result.x = L.s.x + (L.e.x - L.s.x)*t;
        result.y = L.s.y + (L.e.y - L.s.y)*t;
    
    else
    
        if(dist(P,L.s) < dist(P,L.e))
        result = L.s;
        else result = L.e;
    
    return result;


double find2(line L,double rr,double l,double r)    //圆心区间左点   这里用二分法找;理论上讲以这个点为圆心的圆与线段相切

    double m;
    while (r-l>1e-6)
    
        m=(l+r)/2;
        if (dist(NearestPointToLineSeg(point(m,0),L),point(m,0))<rr) r=m;  //m到线段距离小于r,则需要左移; 直到刚好切
        else l=m;
    
    return (l+r)/2;


double find3(line L,double rr,double l,double r)  //圆心区间右点

    double m;
    while (r-l>1e-6)
    
        m=(l+r)/2;
        if (dist(NearestPointToLineSeg(point(m,0),L),point(m,0))>rr) r=m;
        else l=m;
    
    return (l+r)/2;


bool cmp(st a,st b)                                  //圆心不能在的区间先左再右排序

    if (fabs(a.l-b.l)<1e-6) return a.r<b.r;
    else return a.l<b.l;


bool ok(double h)                                  //这个半径下能不能满足题意为空圆 false为可以

    sort(v.begin(),v.end(),cmp);
    if (v.empty()) return false;                       //可以 注意go函数时!ok()
    if (v[0].l+eps>0) return false;                    //0-v[0]l 有空间做圆心
    double r=v[0].r;
    int i=0;
    while (i<(int)v.size()-1 && (v[i+1].l+eps<r || v[i+1].l<0))  //区间没有空隙
    
        i++;
        r=max(r,v[i].r);
    
    if (r+eps<h) return false;
    else return true;



int go(double rr)

    v.clear();
    for (int i=1;i<=n;i++)
    
        double len;
        double mid;
        double l,r;
        if (c[i].s.y*c[i].e.y>0)                    //x轴同侧
        
            if (fabs(c[i].s.y)>fabs(c[i].e.y))
            
                len=fabs(c[i].e.y);                    //而不是abs  到x轴的距离   len>r时就不需要考虑这条线段;否则要找到一个区间,圆心不能在区间内
                mid=c[i].e.x;
            
            else
            
                len=fabs(c[i].s.y);  
                mid=c[i].s.x;
            
        
        else                                       //异侧
        
            len=0;                                 //点在x轴上,距离为0
            mid=c[i].s.x+fabs((c[i].s.y/(c[i].e.y-c[i].s.y)*(c[i].e.x-c[i].s.x)));  //交点x坐标   用相似三角形求
        
        if (len<rr)                       //len<rr,需要考虑这条线段
        
            l=find2(c[i],rr,-3e4,mid);    //圆心区间的左点
            r=find3(c[i],rr,mid,3e4);     //圆心区间的右点
            st x;
            x.l=l;x.r=r;
            v.push_back(x);
        
    
    return !ok(L);


double find1()                //二分半径

    double l=0,r=2e4;
    double m;
    while (r-l>1e-6)
    
        m=(l+r)/2;
        if (go(m)==1) l=m;    //半径为m可以,就再加长一点
        else r=m;
    
    return (l+r)/2;


int main()

    //freopen("c.in","r",stdin);
    scanf("%d",&t);
    while (t--)
    
        scanf("%d%d",&n,&L);
        for (int i=1;i<=n;i++)
        
            scanf("%lf%lf%lf%lf",&c[i].s.x,&c[i].s.y,&c[i].e.x,&c[i].e.y);  //e点在右边
            if (c[i].s.x>c[i].e.x) swap(c[i].e,c[i].s);
        
        printf("%.3f\\n",find1());
    
    return 0;

 

以上是关于Gym 101464C - 计算几何+二分(uva1463)的主要内容,如果未能解决你的问题,请参考以下文章

Gym - 101845D 计算几何

Angle Beats Gym - 102361A(计算几何)

计算几何多边形面积——swerc Gym - 102501F

Rasheda And The Zeriba Gym - 100283A ? 计算几何

Gym102361A Angle Beats(直角三角形 计算几何)题解

Gym 101917 E 简单计算几何,I 最大流