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;
}

原文地址:https://www.cnblogs.com/lqerio/p/11117537.html

时间: 2024-10-12 19:56:39

Gym 101464C - 计算几何+二分(uva1463)的相关文章

【POJ】2318 TOYS ——计算几何+二分

TOYS Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 10281   Accepted: 4924 Description Calculate the number of toys that land in each bin of a partitioned toy box. Mom and dad have a problem - their child John never puts his toys away w

【bzoj1822】[JSOI2010]Frozen Nova 冷冻波 计算几何+二分+网络流最大流

题目描述 WJJ喜欢“魔兽争霸”这个游戏.在游戏中,巫妖是一种强大的英雄,它的技能Frozen Nova每次可以杀死一个小精灵.我们认为,巫妖和小精灵都可以看成是平面上的点. 当巫妖和小精灵之间的直线距离不超过R,且巫妖看到小精灵的视线没有被树木阻挡(也就是说,巫妖和小精灵的连线与任何树木都没有公共点)的话,巫妖就可以瞬间杀灭一个小精灵. 在森林里有N个巫妖,每个巫妖释放Frozen Nova之后,都需要等待一段时间,才能再次施放.不同的巫妖有不同的等待时间和施法范围,但相同的是,每次施放都可以

POJ 2398 计算几何+二分+排序

Toy Storage Time Limit: 1000MS  Memory Limit: 65536K Total Submissions: 3953  Accepted: 2334 Description Mom and dad have a problem: their child, Reza, never puts his toys away when he is finished playing with them. They gave Reza a rectangular box t

POJ 2318 计算几何+二分

TOYS Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 10425 Accepted: 5002 Description Calculate the number of toys that land in each bin of a partitioned toy box. Mom and dad have a problem - their child John never puts his toys away when

Codeforces gym 100517(二分,同方向判断)

题意:给了一个凸包,按顺时针顺序给点,点数不超过10万,再给了两个不同点,点严格在凸包内,凸包保证没有三点共线,问凸包上有多少对点(pi, pj),满足pi和pj的线段 与 两个点的线段严格相交,线段间严格相交意思是交点不在端点. 链接:http://codeforces.com/gym/100517 (K题) 解法:设凸包有n个点,将凸包点集p扩大一倍,变为2n个点.枚举前n个点,每次枚举到 i ,在[i+1, i+n-1]内进行二分,找到两个点p1,p2,满足p1和p2是"最靠近"

Gym 100886J Sockets 二分答案 + 贪心

Description standard input/outputStatements Valera has only one electrical socket in his flat. He also has m devices which require electricity to work. He's got n plug multipliers to plug the devices, the i-th plug multiplier has ai sockets. A device

Gym 101055A 计算几何,暴力

http://codeforces.com/gym/101055/problem/A 题目:给定一些三维空间的点,要你找一个平面,能覆盖尽量多的点,只要求输出点数即可.n<=50 因为数据量小,我们考虑暴力. 首先,三个不在同一条直线的点,确定一个平面,然后枚举其他的点.判断一下,这样的复杂度是n^4.可以接受 特判.所有点都在同一条直线.直接输出n. 后面的,枚举三个点后,能算出这个平面的法向量,然后枚举其他点,与法向量的数量积是0的,就可以ans++ #include <cstdio>

hdu 4033 Regular Polygon 计算几何 二分+余弦定理

题目链接 给一个n个顶点的正多边形, 给出多边形内部一个点到n个顶点的距离, 让你求出这个多边形的边长. 二分边长, 然后用余弦定理求出给出的相邻的两个边之间的夹角, 看所有的加起来是不是2Pi. #include <iostream> #include <vector> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #includ

hdu 3264 09 宁波 现场 E - Open-air shopping malls 计算几何 二分

Description The city of M is a famous shopping city and its open-air shopping malls are extremely attractive. During the tourist seasons, thousands of people crowded into these shopping malls and enjoy the vary-different shopping. Unfortunately, the