题意:给出四面体的四个顶点,求出其内切球的球心坐标和半径,如果不存在内切球,输出"O O O O"。
tags:一堆公式。。可以做模板了
我们可以将平面上的四点得到由同一个点出发的三个矢量。这样就可以计算这三个矢量的混合积M,则M/6即为四面体体积V。
题目无解的情况当且仅当四点共面,即混合积为0。
求得四面体体积后,可以根据公式r = 3V/(S1+S2+S3+S4)得到内切球半径, S1~S4为四面体四个面的面积。
当前的问题转化为如何求四面体四个面的面积。
由于我们已经知道四个顶点的坐标,因此可以通过海伦公式来分别求解四个面的面积S1~S4,以此来求得r。
对于球心坐标,有公式:
x=(p1.x*s1+p2.x*s2+p3.x*s3+p4.x*s4)/(s1+s2+s3+s4);
y=(p1.y*s1+p2.y*s2+p3.y*s3+p4.y*s4)/(s1+s2+s3+s4);
z=(p1.z*s1+p2.z*s2+p3.z*s3+p4.z*s4)/(s1+s2+s3+s4);
si为pi做顶点时,对应的底面的面积。
// HDU 5733 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a;i<=b;i++) #define per(i,b,a) for (int i=b;i>=a;i--) #define mes(a,b) memset(a,b,sizeof(a)) #define INF 0x3f3f3f3f typedef long long ll; const int N = 200005; const double eps=1e-8; double s[4], sum, x, y, z, v; struct Point { double x, y, z; Point operator - (const Point &b) const { //向量 a 与 b 的点积 return Point{x-b.x, y-b.y, z-b.z}; } Point operator ^ (const Point &b) const { //向量 a 与 b 的叉积 return Point{(y*b.z-z*b.y), (z*b.x-x*b.z), (x*b.y-y*b.x)}; } } p[4]; int dcmp(double x) { // x为混合积,判正负,x==0表示4点共面 if(fabs(x)<eps) return 0; return x<0?-1:1; } double MixMul(Point a, Point b, Point c) { //求向量a,b,c的混合积 return a.x*b.y*c.z+a.y*b.z*c.x+a.z*b.x*c.y-a.x*b.z*c.y-a.y*b.x*c.z-a.z*b.y*c.x; } double dis(Point a, Point b) { //两点距离 return sqrt(pow(a.x-b.x, 2)+pow(a.y-b.y, 2)+pow(a.z-b.z, 2)); } double Heron(double a, double b, double c) { //海伦公式求三角形面积 double p=(a+b+c)/2; return sqrt(p*(p-a)*(p-b)*(p-c)); } double getsqr(Point a, Point b, Point c) { //通过海伦公式求三个向量构成的面的面积 return Heron(dis(a,b), dis(b,c), dis(a,c)); } int main() { while(~scanf("%lf %lf %lf", &p[0].x, &p[0].y, &p[0].z)) { rep(i,1,3) scanf("%lf %lf %lf", &p[i].x, &p[i].y, &p[i].z); v=fabs(MixMul(p[0]-p[1], p[0]-p[2], p[0]-p[3])); if(dcmp(v)==0) { puts("O O O O"); continue; } s[3]=getsqr(p[0], p[1], p[2]); // 分别求出4个面的面积s[i] s[2]=getsqr(p[0], p[1], p[3]); s[1]=getsqr(p[0], p[2], p[3]); s[0]=getsqr(p[1], p[2], p[3]); sum=0; rep(i,0,3) s[i]=fabs(s[i]), sum+=s[i]; // sum为四个面的面积和,内切球半径 = v/2/sum x=y=z=0; rep(i,0,3) { x+=p[i].x*s[i]; y+=p[i].y*s[i]; z+=p[i].z*s[i]; } x/=sum, y/=sum, z/=sum; //内切球球心坐标公式 printf("%.4f %.4f %.4f %.4f ", x, y, z, v/2/sum); } return 0; }