1. 程式人生 > >hdu 5531 Rebuild(三分)

hdu 5531 Rebuild(三分)

題目連結:

題目大意:

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