【题目大意】:给出一堆三维的点,求一个最小的圆锥覆盖所有的点。
【解题思路】:这道题上一次做是暑假之后的一次组队赛的题目。其实自从某一次yy到三分极值这种方法之后,顿悟。队友说,一切三分均可秒。
哎...我自惭形秽啊...
首先,圆锥任何一个沿高的截面的形状都是一样的,所以对于每一个点都会在某一个截面上,而截面均相同。
这样,我们完成了首步,某种程度上类似于降维的思想。其次,我们发现截面的一半都是一个直角三角形。
在最小圆锥的这种情况下,必定有至少一个点位于截面三角形的斜边上,而这个点,势必是最外层的点。根据相似三角形的定义,我们可以由直径推出高,或由高推出直径,而且半径越长,高越短,反亦然。
因此,这显然不是一个单调的问题,故而考虑三分极值,因为必定存在一个半径和高满足体积最小这一条件。
【代码】:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <queue> #include <cmath> #include <string> #include <cctype> #include <map> #include <iomanip> using namespace std; #define eps 1e-8 #define pb push_back #define lc(x) (x << 1) #define rc(x) (x << 1 | 1) #define lowbit(x) (x & (-x)) #define ll long long struct Point{ double x, y, z, rr; Point() {} Point(double a, double b, double c){ x = a, y = b, z = c, rr = sqrt(x * x + y * y); } }point[20000]; double maxr,ansr,ansh,ansh1; int n; double check(double r){ double maxx=-1.0; for (int i=0; i<n; i++){ double z=point[i].z,rr=point[i].rr; double k=point[i].z/(r-point[i].rr); if (maxx<k) {maxx=k;} } return maxx*r; } int main() { int T; scanf("%d",&T); while (T--) { scanf("%d", &n); maxr=0.0; double x, y, z; for (int i=0; i<n; i++){ scanf("%lf%lf%lf",&x,&y,&z); point[i]=Point(x, y, z); if (point[i].rr>maxr) maxr=point[i].rr; } double low=maxr,high=11000,mid,mmid; while (low+eps<high) { mid=(low+high)/2.0; mmid=(mid+high)/2.0; double k1,k2; k1=check(mid); k2=check(mmid); if (k1*mid*mid<k2*mmid*mmid) { high=mmid; ansr=mid; ansh=k1; } else { low=mid; ansr=mmid; ansh=k2; } } printf("%.3f %.3f\n", ansh, ansr); } return 0; }