题目大意:
在一个立体的空间内有n个点(x,y,z),满足z>=0。
现在要你放一个体积尽量小的圆锥,把这些点都包住。
求圆锥的高和底面半径。
思路:
因为圆锥里面是对称的,因此问题很容易可以转化到一个二维平面上,我们只需要将所有点绕着z轴旋转到xOz平面上即可。
考虑不同半径时圆锥的体积,不难发现这是一个关于半径r的下凸函数。
于是我们可以三分求解。
对于当前分出来的两个半径,我们可以O(n)枚举每个点算出高度,然后看一下哪边体积小就继续分哪边。
1 #include<cmath> 2 #include<cstdio> 3 #include<cctype> 4 #include<algorithm> 5 inline int getint() { 6 register char ch; 7 while(!isdigit(ch=getchar())); 8 register int x=ch^'0'; 9 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 10 return x; 11 } 12 const double eps=5e-5; 13 const int N=10001; 14 struct Point { 15 double x,y; 16 }; 17 Point p[N]; 18 inline double sqr(const double &x) { 19 return x*x; 20 } 21 int n; 22 inline double calc(const double &r) { 23 double h=0; 24 for(register int i=0;i<n;i++) { 25 h=std::max(h,(r*p[i].y)/(r-p[i].x)); 26 } 27 return h; 28 } 29 int main() { 30 for(register int T=getint();T;T--) { 31 n=getint(); 32 double l=0,r=1e4; 33 for(register int i=0;i<n;i++) { 34 double x,y,z; 35 scanf("%lf%lf%lf",&x,&y,&z); 36 p[i]=(Point){sqrt(sqr(x)+sqr(y)),z}; 37 l=std::max(l,p[i].x); 38 } 39 while(r-l>eps) { 40 const double mid1=(l*2+r)/3,mid2=(l+r*2)/3; 41 if(calc(mid1)*sqr(mid1)<calc(mid2)*sqr(mid2)) { 42 r=mid2; 43 } else { 44 l=mid1; 45 } 46 } 47 const double ans=(l+r)/2; 48 printf("%.3f %.3f ",calc(ans),ans); 49 } 50 return 0; 51 }