hdu 5531 Rebuild(三分)

7 篇文章 0 订阅

题目链接:

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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值