HDU 5531 Rebuild 题解

xiaoxiao2021-02-27  173

题意

给定一个n边形,问是否可以以每个顶点为圆心画圆使得相邻顶点的圆外切,如果可以,问这些圆面积总和最小是多少,在总和最小的情况下每个圆面积是多少

思路

首先容易发现当一个圆半径固定,其它的也就固定了,不妨设以第一个点为圆心的圆半径为x,通过列式子可以发现n是奇数的情况与偶数的情况不同,所以要分类讨论。对于n是奇数的情况,x只能是固定的值,把它带进去计算,如果在某一条边上出现了负长度或超出了长度,那么就是无解,否则就把唯一解算出来。对于n是偶数的情况,通过计算可以发现这个n边形的边长需要满足一个条件,如果不行就是无解,其次可以通过在每一条边上半径不能为负且不能超出该边可以算出x的范围,而这个面积总和通过计算可知是一个二次函数,那么就是一个二次函数求区间最小值的问题,因为函数并不复杂,所以用中学的方法可以判断出x取何值时函数值最小,那么把它带入计算即可

代码

#include <cstdio> #include <cmath> #include <algorithm> #define eps 1e-6 using namespace std; int x[10001],y[10001]; double dis[10001]; int t,n; double xx,ans,l,r,temp,mid,add; double calc(double x) { double s=0; for(int i=0;i<n;i++) { s+=x*x; if(x<0||x>dis[i]||x>dis[(i-1+n)%n]) return -1; x=dis[i]-x; } return acos(-1)*s; } int main() { scanf("%d",&t); while(t--) { scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d%d",&x[i],&y[i]); for(int i=0;i<n;i++) dis[i]=sqrt((x[(i+1)%n]-x[i])*(x[(i+1)%n]-x[i])+(y[(i+1)%n]-y[i])*(y[(i+1)%n]-y[i])); if(n%2==1) { xx=0; for(int i=0;i<n;i++) if(i%2==0) xx+=dis[i]; else xx-=dis[i]; xx/=2; ans=calc(xx); if(ans>0) { printf("%.2f\n",ans); for(int i=0;i<n;i++) { printf("%.2f\n",xx); xx=dis[i]-xx; } } else printf("IMPOSSIBLE\n"); } else { l=0; r=dis[0]; temp=0; for(int i=0;i<n;i++) { //printf("%.2f %.2f %.2f %.2f\n",l,r,temp,dis[i]); if(i%2==0) { temp+=dis[i]; r=min(r,temp); } else { temp-=dis[i]; l=max(l,temp); } } if(r<l) { printf("IMPOSSIBLE\n"); continue; } add=0; for(int i=0;i<n-1;i++) if(i%2==0) add+=dis[i]; else add-=dis[i]; if(fabs(add-dis[n-1])>eps) { printf("IMPOSSIBLE\n"); continue; } mid=0; for(int i=0;i<n-1;i++) if(i%2==0) mid+=(n-1-i)*dis[i]; else mid-=(n-1-i)*dis[i]; mid/=n; if(l<=mid&&r>=mid) { printf("%.2f\n",calc(mid)); xx=mid; for(int i=0;i<n;i++) { printf("%.2f\n",xx); xx=dis[i]-xx; } } else { if(fabs(l-mid)<=fabs(r-mid)) xx=l; else xx=r; printf("%.2f\n",calc(xx)); for(int i=0;i<n;i++) { printf("%.2f\n",xx); xx=dis[i]-xx; } } } } return 0; }
转载请注明原文地址: https://www.6miu.com/read-15333.html

最新回复(0)