版权声明:本文为博主原创文章,未经博主允许不得转载。 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;
}