ACM-ICPC Shenyang Machining Disc Rotors(圆)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/leekerian/article/details/89320033

题意

给出一个过原点的圆用一些圆去切割这个圆,求出剩下区域的直径,直径为圆上最长两点的距离(保证切割区域不会相交)

题解

思路很简单,但是细节比较多:我们首先用atan2算出每个圆的圆心角度对于在第三与第四象限的点为(0到-180)所以我们转化成(180-360)只需要+-360就可以。用余弦定理求出交点的角度,然后对数组排序,遍历数组从中取出剩下区域的角度放到另外一个数组中p2,O(n^2)遍历p2对于每个剩下的区间加减180求出对称的点存不存在(不是求对称区间)存在就是直径,否则求出所有的长度取max

#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;


const double eps=1e-8;
const double pi=acos(-1.0);
int sgn(double x)
{
    if(fabs(x)<eps) return 0;
    if(x<0) return -1;
    else return 1;
}
struct Point
{
    double x,y,r;
    Point(){}
    Point(double _x,double _y){ x=_x;y=_y;}
    Point(double _x,double _y,double _r){ x=_x;y=_y;r=_r;}
    Point operator -(const Point &b)const{
        return Point(x-b.x,y-b.y);
    }
    double operator *(const Point &b)const{
        return x*b.x+y*b.y;
    }
};

double dis(Point &a,Point &b)
{
    return sqrt((a-b)*(a-b));
}

bool cmp(Point a,Point b)
{
    return a.x<b.x;
}
double ans;
int n;
double R;
void cal(double a,double b){
    double d=fabs(a-b);
    if(d>pi) d=2*pi-d;//360-大的角度
    ans=max(ans,R*sin(d*0.5)*2.0);
}

Point p[111];
Point p1[333];
Point p2[333];
int main()
{
    int t;
    cin>>t;
    int kace=1;
    while(t--)
    {
        ans=-1.0;
        scanf("%d%lf",&n,&R);
        for(int i=0;i<n;i++)
            scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].r);
        Point cir=Point(0,0,R);
        int cont=0;
        for(int i=0;i<n;i++)
        {
            double len=dis(p[i],cir);
            if(sgn(len-R-p[i].r)>=0||sgn(R-(len+p[i].r))>=0||sgn(p[i].r-(len+R))>=0) continue;//两圆外离,两种大小圆的内含
            double at1=atan2(p[i].y,p[i].x);
            if(sgn(at1-0)<0) at1+=2*pi;//如果是负的角度转成180-360
            double at2=acos((len*len+R*R-p[i].r*p[i].r)/(2*R*len));
            double at3_1=at1+at2;
            double at3_2=at1-at2;
            if(sgn(at3_1-2*pi)>0) at3_1-=2*pi;
            if(sgn(at3_2-0)<0) at3_2+=2*pi;
            p1[cont++]=Point(at3_2,at3_1);
        }
        int cont1=0; 
        sort(p1,p1+cont,cmp);
        for(int i=0;i<cont;i++)
            p2[cont1++]=Point(p1[i].y,p1[(i+1)%cont].x);
        // for(int i=0;i<cont1;i++)
        //     cout<<p2[i].x<<" "<<p2[i].y<<endl;
        int fg=0;
        for(int i=0;i<cont1;i++)
        {
            double x1,y1;
            if(sgn(p2[i].x-pi)<=0) x1=p2[i].x+pi;
            else x1=p2[i].x-pi;
            if(sgn(p2[i].y-pi)<=0) y1=p2[i].y+pi;
            else y1=p2[i].y-pi;
            for(int j=0;j<cont1;j++)
            {
                //if(i==j) continue;这里不能continue否则少考虑一种情况
                if(p2[j].x<p2[j].y&&p2[j].x<=x1&&x1<=p2[j].y)//只要找出对称点存在就说明有直径,不用找对称区间。
                    fg=1;
                if(p2[j].x<p2[j].y&&p2[j].x<=y1&&y1<=p2[j].y)
                    fg=1;
                if(p2[j].x>p2[j].y&&(p2[j].x<=x1||p2[j].y>=x1))//横跨0-360的区间情况
                    fg=1;
                if(p2[j].x>p2[j].y&&(p2[j].x<=y1||p2[j].y>=y1))
                    fg=1;
                cal(p2[i].x,p2[j].x);
                cal(p2[i].x,p2[j].y);
                cal(p2[i].y,p2[j].x);
                cal(p2[i].y,p2[j].y);
            }
            if(fg) break;
        }
        if(fg) ans=2*R;
        if(cont==0) ans=2*R;
        printf("Case #%d: %.15lf\n",kace++,ans);     
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/leekerian/article/details/89320033