HDU 3264 Open-air shopping malls

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3264

题目大意:给出平面上n个圆的圆心和半径,求一个大圆,它的圆心与某个给定圆的圆心重合,且对于每一个给定的圆,大圆至少覆盖面积的一半,求出满足要求的大圆的最小半径。

分析:由于所求的大圆圆心只有n种选择,我们可以枚举大圆的圆心,然后求能够满足要求的最小半径,取半径最小的方案即可。

  直接求满足要求的最小半径比较困难,但若我们已经知道一个半径,判断它时候覆盖了每一个圆至少一半的面积,问题就简单那多了。如果不会求圆的重合面积的话请移步                        http://www.cnblogs.com/evan-oi/archive/2012/03/14/2395989.html

所以枚举圆心之后,二分查找圆的最小半径就可以了。总时间复杂度为O(n^2logL),L为查找的区间长度(对于这种n奇小的题目来说复杂度基本上无视,只要不是指数级就应该都能过)

 

附代码:

View Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;
#define MaxN 25
#define INF 20000000
#define pi 3.141592653
#define zero 1e-8
struct atp
{
double x,y,r;
}p[MaxN];
int n;
double ans;
void init()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].r);
}

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

double calc(atp a,double ra,atp b)
{
double t=0,d,rb=b.r;
d=dis(a,b);
if (ra<rb) swap(ra,rb);
if (d>=ra+rb) return 0;
if (d<=ra-rb) return pi*rb*rb;

double t1=acos((ra*ra+d*d-rb*rb)/(2*ra*d));
double t2=acos((rb*rb+d*d-ra*ra)/(2*rb*d));
t=t1*ra*ra+t2*rb*rb-d*ra*sin(t1);
return t;
}

bool check(int x,double r)
{
for (int i=1;i<=n;i++)
if (2*calc(p[x],r,p[i]) <pi*p[i].r*p[i].r) return false;
return true;
}

double get(int i)
{
double l,r,mid;
l=0;r=30000;
while (l+zero<=r)
{
mid=(l+r)/2;
if (check(i,mid)) r=mid-zero; else l=mid+zero;
}
return mid;
}

void work()
{
double tans;ans=INF;
for (int i=1;i<=n;i++)
{tans=get(i);if (ans>tans) ans=tans;}
printf("%.4lf\n",ans);
}
int main()
{
freopen("in","r",stdin);
freopen("out","w",stdout);
int Case;
scanf("%d",&Case);
while (Case--)
init(),work();
return 0;
}
posted @ 2012-03-14 15:34  Evan1004  阅读(411)  评论(0编辑  收藏  举报