HDOJ 3264 Open-air shopping malls 计算几何 二分

时间:2021-01-10 08:45:52

计算几何的圆的模板题+二分判断

题目中给了n个圆,要求:在这n个圆中取某个圆的圆心,然后找到最小的半径,使得这个新的圆与这n个圆的相交面积,会不小于这n个圆的面积的一半

说起来,很绕口,但是因为n不大,是可以枚举的!

对于每个圆心,我们都求一个最小的半径,然后n个值中间取最小的就是答案

那么,如何求得这个最小呢?很简单,化计算性问题为判定性问题

我们二分半径,然后验证这个半径会不会满足题中面积的条件

代码如下:

#include<bits/stdc++.h> 
using namespace std;  

int t,n;  
double ans;  

const int maxn=30;  
const double eps=1e-6;  
const double pi=acos(-1.0);  
int sgn(double x){  
    if (fabs(x)<eps) return 0;  
    if (x<0) return -1;  
    return 1;  
}  

struct Point{  
    double x,y;  
    Point(){}  
    Point(double _x,double _y){  
        x=_x;  
        y=_y;  
    }  
    double distance(Point p){  
        return hypot(x-p.x,y-p.y);  
    }  
};  

struct circle{  
    Point p;  
    double r;  
    circle(){}  
    circle(Point _p,double _r){  
        p=_p;  
        r=_r;  
    }  
    circle(double x,double y,double _r){  
        p=Point(x,y);  
        r=_r;  
    }  
    double area(){  
        return pi*r*r;  
    }  
    int relationcircle(circle v){  
        double d=p.distance(v.p);  
        if (sgn(d-r-v.r)>0) return 5;  
        if (sgn(d-r-v.r)==0) return 4;  
        double l=fabs(r-v.r);  
        if (sgn(d-r-v.r)<0&&sgn(d-l)>0) return 3;  
        if (sgn(d-l)==0) return 2;  
        if (sgn(d-l)<0) return 1;  
    }  
    double areacircle(circle v){  
        int rel=relationcircle(v);  
        if (rel>=4) return 0.0;  
        if (rel<=2) return min(area(),v.area());  
        double d=p.distance(v.p);  
        double hf=(r+v.r+d)/2.0;  
        double ss=2*sqrt(hf*(hf-r)*(hf-v.r)*(hf-d));  
        double a1=acos((r*r+d*d-v.r*v.r)/(2.0*r*d));  
        a1=a1*r*r;  
        double a2=acos((v.r*v.r+d*d-r*r)/(2.0*v.r*d));  
        a2=a2*v.r*v.r;  
        return a1+a2-ss;  
    }  
}c[maxn];  

bool ok(){  
    for(int i=1;i<=n;i++){  
        double a1=c[n+1].areacircle(c[i]);  
        double a2=c[i].area();  
        if (a2-2*a1>eps) return false;  
    }  
    return true;  
}  

int main(){  
    //freopen("input.txt","r",stdin); 
    double x,y,r;  
    scanf("%d",&t);  
    while(t--){  
        ans=100000.0;  
        scanf("%d",&n);  
        for(int i=1;i<=n;i++){  
            scanf("%lf%lf%lf",&x,&y,&r);  
            c[i]=circle(x,y,r);  
        }  
        for(int i=1;i<=n;i++){  
            double L=0,R=10000,mid;  
            while(L+eps<=R){  
                mid=(L+R)/2.0;  
                c[n+1]=circle(c[i].p,mid);  
                if (ok()) R=mid;  
                else L=mid;  
            }  
            ans=min(ans,mid);  
        }  
        printf("%.4lf\n",ans);  
    }  
    return 0;  
}