HDU5733 tetrahedron(计算几何)
(本题解由 ljw 提供)
题意:给四个点,问是不是四面体,如果是四面体则给出内切球的球心坐标和球的半径。
范围: x , y x, y x,y 坐标 ∈ [ − 1 e 6 , 1 e 6 ] \in [-1e6, 1e6] ∈[−1e6,1e6]
分析:判断四个点是否共面即可判断是不是四面体,这里可以利用行列式的值计算(不会的去复习高数下的前几章和线代的相关内容)。
我们令 S = S △ A B C + S △ A B D + S △ A C D + S △ B C D S=S_{\triangle ABC}+S_{\triangle ABD}+S_{\triangle ACD}+S_{\triangle BCD} S=S△ABC+S△ABD+S△ACD+S△BCD
则可以得到内切球球心的表达式为:
x = ( S △ A B C ∗ D . x + S △ A B D ∗ C . x + S △ A C D ∗ B . x + S △ B C D ∗ A . x ) S x=(\frac{S_{\triangle ABC}*D.x+S_{\triangle ABD}*C.x+S_{\triangle ACD}*B.x+S_{\triangle BCD}*A.x)}{S} x=(SS△ABC∗D.x+S△ABD∗C.x+S△ACD∗B.x+S△BCD∗A.x)
y = ( S △ A B C ∗ D . y + S △ A B D ∗ C . y + S △ A C D ∗ B . y + S △ B C D ∗ A . y ) S y=(\frac{S_{\triangle ABC}*D.y+S_{\triangle ABD}*C.y+S_{\triangle ACD}*B.y+S_{\triangle BCD}*A.y)}{S} y=(SS△ABC∗D.y+S△ABD∗C.y+S△ACD∗B.y+S△BCD∗A.y)
z = ( S △ A B C ∗ D . z + S △ A B D ∗ C . z + S △ A C D ∗ B . z + S △ B C D ∗ A . z ) S z=(\frac{S_{\triangle ABC}*D.z+S_{\triangle ABD}*C.z+S_{\triangle ACD}*B.z+S_{\triangle BCD}*A.z)}{S} z=(SS△ABC∗D.z+S△ABD∗C.z+S△ACD∗B.z+S△BCD∗A.z)
(具体证明可以去找高中竞赛教辅)
之后即可利用内切球心到四面体的距离相等,利用四个面的面积和整个四面体的体积即可得到球的半径。( r ∗ S = S △ A B C ∗ h r*S = S_{\triangle ABC}*h r∗S=S△ABC∗h, h h h为 D D D到 S △ A B C S_{\triangle ABC} S△ABC的距离)
Notice:不存在的时候输出为四个O,不是0
Code:
#include <cstdio>
#include <cmath>
#include <vector>
using namespace std;
struct Point{
double x, y, z;
Point(double x,double y,double z):x(x),y(y),z(z){}
Point(){
}
}point[4];
// 判断是否共面
int judge(Point point[])
{
double x[4], y[4], z[4];
for(int i = 1; i < 4; i++){
x[i] = point[i].x - point[0].x;
y[i] = point[i].y - point[0].y;
z[i] = point[i].z - point[0].z;
}
return (x[1] * y[2] * z[3]) + (x[2] * y[3] * z[1]) + (x[3] * y[1] *z[2]) - (x[3] * y[2] * z[1]) - (y[3] * z[2] * x[1]) - (z[3] * x[2] * y[1]);
}
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 area(Point a, Point b, Point c){
double ab = dis(a, b);
double ac = dis(a, c);
double bc = dis(b, c);
double p = (ab + ac + bc) / 2;
return sqrt(p * (p - ab) * (p - ac) * (p - bc));
}
// 球心坐标
vector<double> cal(Point point[])
{
vector <double> ans;
ans.resize(3);
for(int i = 0; i < 3; i++){
for(int j = 0; j < 4; j++){
switch(i){
case 0:
ans[i] += area(point[(j + 1) % 4], point[(j + 2) % 4], point[(j + 3) % 4]) * point[j].x; break;
case 1:
ans[i] += area(point[(j + 1) % 4], point[(j + 2) % 4], point[(j + 3) % 4]) * point[j].y; break;
case 2:
ans[i] += area(point[(j + 1) % 4], point[(j + 2) % 4], point[(j + 3) % 4]) * point[j].z; break;
}
}
}
return ans;
}
Point Cross(Point a, Point b)
{
return Point((a.y * b.z - a.z * b.y), -(a.x * b.z - b.x * a.z), (a.x * b.y - a.y * b.x));
}
double Dot(Point A, Point B)
{
return (A.x * B.x + A.y * B.y + A.z * B.z);
}
int main()
{
while(~scanf("%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf", &point[0].x, &point[0].y, &point[0].z, &point[1].x, &point[1].y, &point[1].z, &point[2].x, &point[2].y, &point[2].z, &point[3].x, &point[3].y, &point[3].z)){
if(judge(point)){
vector<double> ans = cal(point);
double S = 0;
for(int j = 0; j < 4; j++){
S += area(point[(j + 1) % 4], point[(j + 2) % 4], point[(j + 3) % 4]);
}
Point n = Cross(Point(point[0].x - point[1].x, point[0].y - point[1].y, point[0].z - point[1].z), Point(point[2].x - point[1].x, point[2].y - point[1].y, point[2].z - point[1].z));
Point AD = Point(point[0].x - point[3].x, point[0].y - point[3].y, point[0].z - point[3].z);
//printf("%lf %lf %lf\n", n.x, n.y, n.z);
double h = fabs(Dot(AD, n) / sqrt(Dot(n, n)));
double s = area(point[0], point[1], point[2]);
//printf("%f %f %f\n", s, S, h);
printf("%.4f %.4f %.4f %.4f\n", ans[0] / S, ans[1] / S, ans[2] / S, s * h / S);
} else {
puts("O O O O");
}
}
return 0;
}
【END】感谢观看