HDU3264 Open-air shopping malls (圓交+二分)

bigbigship發表於2014-12-11

題目連結:

http://acm.hdu.edu.cn/showproblem.php?pid=3264


題意:

給定n個圓的圓心和半徑,求一個圓心為這些圓中任意一個,與所有圓相交的面積超過其面積一半的圓的最小半徑。


分析:

列舉圓心,然後二分得到最小的半徑,直接套求圓交的模板。


程式碼如下:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;

const double eps = 1e-8;

const double pi = acos(-1.0);

int n;

struct point
{
    double x,y,r;
}cir[25];

double S(point a)
{
    return pi*a.r*a.r;
}

double dis(point a,point b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

double calu(point a,point b)
{
    point tmpa=a,tmpb=b;
    if(tmpa.r<tmpb.r) swap(tmpa,tmpb);
    double area = 0;
    double dd = dis(tmpa,tmpb);
    if(dd>tmpa.r-tmpb.r&&dd<tmpa.r+tmpb.r){
        double cos1 = (tmpa.r*tmpa.r+dd*dd-tmpb.r*tmpb.r)/(2*tmpa.r*dd);
        double cos2 = (tmpb.r*tmpb.r+dd*dd-tmpa.r*tmpa.r)/(2*tmpb.r*dd);
        double th1 = 2*acos(cos1);
        double th2 = 2*acos(cos2);
        double s1 = 0.5*tmpa.r*tmpa.r*sin(th1);
        double s2 = 0.5*tmpb.r*tmpb.r*sin(th2);
        double s3 = (th1/2)*tmpa.r*tmpa.r;
        double s4 = (th2/2)*tmpb.r*tmpb.r;
        area = s3+s4-s1-s2;
    }
    else if(dd<=tmpa.r-tmpb.r)
        area = S(tmpb);
    return area;
}

bool check(point a)
{
    for(int i=0;i<n;i++){
        if(calu(a,cir[i])*2<pi*cir[i].r*cir[i].r)
            return false;
    }
    return true;
}

double bin_search(double l,double r,point tmp)
{
    double mid;
    while(r-l>=eps){
        mid=(l+r)/2.0;
        tmp.r=mid;
        if(check(tmp)) r=mid;
        else l=mid+eps;
    }
    return mid;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%lf%lf%lf",&cir[i].x,&cir[i].y,&cir[i].r);
        point ans;
        double r=1000000;
        for(int i=0;i<n;i++){
            ans.x=cir[i].x,ans.y=cir[i].y;
            r=min(r,bin_search(0,30000,ans));
        }
        printf("%.4lf\n",r);
    }
    return 0;
}