题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5531
题目大意:
给出n个点,这n个点可以连成一个凸多边形。现在以多边形的端点作为圆心,分别做n个圆,要求在同一条线上的端点的圆是相切的。现在要求满足条件以后,计算最小的圆面积总和。如果不能保证条件成立,则输出impossible。
思路:
如果我们知道了第一个点上的圆的半径r,那么如果条件满足,剩下的圆的半径都是可以由r来表示的。所以计算圆的面积最小,我们可以列出式子发现是一个关于r的二次函数。也就是说,我们可以通过三分r的大小,来获得最小面积。
如果在纸上模拟可以发现,n为奇数和偶数的情况是不一样的。
如果n是偶数,如果通过计算可以发现,r会被消掉,然后x3-x2+x1==x4应当是成立的(假设这里n=4),所以如果这个式子不能成立就是impossible,否则就可以三分了。三分之前要先对三分的范围进行计算,如果发现left>right,也是impossible的。范围的计算就是看每条边上的半径的范围,最后计算出一个最为精确的范围。
如果n是奇数,此时r是不能被消掉的,所以我们通过计算这些半径,然后看半径中是否会出现<0的情况,如果有就是Impossble,否则直接计算即可。
代码:
#include<stdio.h>
#include<string.h>
#include<math.h>
#define PI acos(-1.0)
#define eps 0.0000000001
double min(double a,double b)
{
if(a<b)return a;
else return b;
}
double max(double a,double b)
{
if(a>b)return a;
else return b;
}
struct node{
double x,y;
}p[10004];
double bian[10004],r[10004];
int n,flag;
double cal(double x)
{
double sum=x*x;
r[1]=x;
for(int i=1;i<n;i++)
{
sum+=(bian[i]-r[i])*(bian[i]-r[i]);
r[i+1]=bian[i]-r[i];
}
return sum*PI;
}
double solve(double l,double r)
{
double mid,midmid,mid_value,midmid_value;
while(fabs(l-r)>eps)
{
mid=(l+r)/2.0;
midmid=(mid+r)/2.0;
mid_value=cal(mid);
midmid_value=cal(midmid);
// printf("%.2f %.2f\n",mid_value,midmid_value);
if(mid_value<=midmid_value)r=midmid;
else l=mid;
}
mid=(l+r)/2.0;
return cal(mid);
}
int main()
{
int T,i,j,k;
scanf("%d",&T);
while(T--)
{
memset(r,0,sizeof(r));
memset(bian,0,sizeof(bian));
flag=0;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%lf%lf",&p[i].x,&p[i].y);
k=0;
for(i=1;i<n;i++)
{
k++;
bian[k]=sqrt((p[i].x-p[i+1].x)*(p[i].x-p[i+1].x)+(p[i].y-p[i+1].y)*(p[i].y-p[i+1].y));
}
bian[++k]=sqrt((p[1].x-p[n].x)*(p[1].x-p[n].x)+(p[1].y-p[n].y)*(p[1].y-p[n].y));
//计算多边形的边长。
if(n%2)
{
double x;
double sum=0;
r[1]=x;
for(i=1;i<n;i++)
{
if(i%2)sum=sum-bian[i];
else sum=sum+bian[i];
}
x=(bian[n]-sum)/2.0;
r[1]=x;
for(i=1;i<n;i++)
r[i+1]=bian[i]-r[i];
for(i=1;i<=n;i++)
{
if(r[i]<0){
flag=1;
break;
}
}
if(flag)printf("IMPOSSIBLE\n");
else {
sum=0;
for(i=1;i<=n;i++)
sum+=r[i]*r[i];
printf("%.2f\n",sum*PI);
for(i=1;i<=n;i++)
printf("%.2f\n",r[i]);
}
}
else
{
double left,right;
int id=1;
double len=bian[1];
left=0;right=bian[1];
for(i=2;i<n;i++)
{
id++;
if(i%2){
len=bian[id]-len;
right=min(right,len);
}
else {
len=bian[id]-len;
left=max(left,-len);
}
}
//对于范围的计算,是有规律的,可以在纸上模拟出来。
r[1]=bian[1]/2.0;
for(i=2;i<=n;i++)
{
r[i]=bian[i-1]-r[i-1];
}
double ans=solve(left,right);
if(fabs((r[1]+r[n])-bian[n])>eps||left>right)printf("IMPOSSIBLE\n");
else {
printf("%.2f\n",ans);
for(i=1;i<=n;i++)
printf("%.2f\n",r[i]);
}
}
}
return 0;
}