hdu 5531 Rebuild(三分)
阿新 • • 發佈:2019-01-22
題目連結:
題目大意:
給出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; }