前言
又是一道看完题就立马产生思路的水题,不过计算几何都是无比麻烦的,忍了好久终于下定决心要AC了它。忍了好久!
题目大意
平面上有n个圆,给定他们各自的圆心和半径。保证任意两个圆不会互相重叠。现在求一个大圆,他的圆心与某个给定圆的圆心重合,且对于每一个给定的圆,大圆至少覆盖该圆面积的一半。求出满足要求的大圆的最小半径。
思路
枚举每个圆心,利用二分搜索最小半径。
代码
计算几何做得少,相关的命名很不规范,因为在很多地方我就是用中文也很难表达自己的思想,更别说只用一个单词来描述一个方法或变量了。
#include
#include
using namespace std;
#define pi 3.1415926535897932
struct point
{
int x,y,r;
}malls[20];
int n;
double dis(double x1,double y1,double x2,double y2)
//(x1,y1)到(x2,y2)的距离
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
double get_area(double a,double b,double c)
{
double acos_t=acos((a*a+b*b-c*c)/(2*a*b));
return b*b*acos_t-b*b*sin(acos_t*2)/2;
}
bool check(int x,int y,double r)
//判断以圆x为的圆心为圆心,半径为r的圆能否覆盖圆y一半面积。
{
double d=dis(malls[x].x,malls[x].y,malls[y].x,malls[y].y);
if(d>=r+malls[y].r)
//若两圆不相交
return false;
if(dmalls[y].r)
//若当前半径为r的圆包含圆y
return true;
else if(r*r*2>=malls[y].r*malls[y].r)
//若圆y包含半径为r的圆,且覆盖面积不小于圆y的一半。
return true;
else
return false;
//两圆相交时
double area=get_area(d,r,malls[y].r)+get_area(d,malls[y].r,r);
return area-malls[y].r*malls[y].r*pi/2>=0?true:false;
}
double find_r(int x,int y)
//寻找以圆x为的圆心为圆心,覆盖圆y至少一半面积的圆的最小半径。
{
double low=0,high=40000;
while(high-low>1e-6)
{
double mid=(low+high)/2;
if(check(x,y,mid))
high=mid;
else
low=mid;
}
return low;
}
double solved(int x)
//寻找以圆x的圆心为圆心,覆盖所有圆至少一半面积的最小半径。
{
double max=0;
for(int i=0;it?max:t;
}
return max;
}
int main()
{
int t;
for(scanf("%d",&t);t>0;t--)
{
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d%d%d",&malls[i].x,&malls[i].y,&malls[i].r);
double min=40000;
for(int i=0;i<n;i++)
{
double t=solved(i);
min=min<t?min:t;
}
printf("%.4lf\n",min);
}
}
附上题目作者的标程:
#include
#include
#include
using namespace std;
const int MAXN=50;
const double PI=3.1415926535897932;
const double ZERO=1e-8;
int n;
double x[MAXN],y[MAXN],r[MAXN];
void init()
{
scanf("%d",&n);
for(int i=1;i=r1+r2)
return false;
if(d-ZEROr2)
return true;
else if(r1*r1*2+ZERO>=r2*r2)
return true;
else
return false;
}
double value=fusiform(r1,r2,d)+fusiform(r2,r1,d);
if(value*2>=r2*r2*PI)
return true;
else
return false;
}
bool check(double ox,double oy,double rr)
{
for(int i=1;i1e-6)
{
mid=(l+r)/2;
if(check(ox,oy,mid))
r=mid;
else
l=mid;
}
return l;
}
void work()
{
double ans,value;
ans=1e+10;
for(int i=1;ivalue)
ans=value;
}
printf("%.4lf\n",ans);
}
int main()
{
int t;
for(scanf("%d",&t);t>0;t--)
{
init();
work();
}
}
后记
又是一道前前后后三天才AC的题,但是09年宁波赛区有一半的队伍现场就切了这道水题。
相比于大一刚接触ACM时能用一整天思考一道trick,现在连模拟题都懒得想。对于这种麻烦的题更是没有调试的心思,find(x,i)写成了find(i,x),这么个小bug找了2天有余…
为什么这么浮躁?大概还是太急功近利了吧。大一时从不想着拿什么奖,只是喜欢切题。现在一门心思想拿奖,做题反而成了负担,遇到题就想尽快AC掉而不是好好思考。
看来近期得努力让自己放松下来才行,唉~