📜  门|门CS 2011 |第 65 题(1)

📅  最后修改于: 2023-12-03 14:58:37.203000             🧑  作者: Mango

题目简介

本题是门|门CS 2011的第65题,题目名称为“射线和球体”。

题目描述

在三维空间中,存在一个球体和若干条射线,求每条射线能够穿过多少个球体。

输入描述

第一行为两个整数n和m,表示球体的数量n和射线的数量m。

接下来n行,每行有4个整数,依次表示球体的中心坐标(x,y,z)和半径r。

接下来m行,每行有6个整数,依次表示射线的起点坐标(x1,y1,z1)和终点坐标(x2,y2,z2)。

输出描述

输出m行,每行一个整数,依次表示每条射线穿过的球体数量。

示例输入

3 2
0 0 0 3
5 5 5 1
10 10 10 2
3 3 3 6 6 6
7 7 7 12 12 12

示例输出

1
2

解题思路

对于每一条射线,我们需要遍历所有的球体来判断是否穿过。我们可以通过先判断这条射线所在的直线是否与球体相交,判断相交的公式如下:

dist = |(P1 - C) × (P2 - C)| / |P2 - P1|  

其中,P1和P2表示射线的起点和终点,C表示球体的中心点,|x|表示x的模长。如果dist小于球体的半径r,则射线穿过了球体。

实现时,我们可以将球体信息存在一个结构体数组中,每次遍历时逐个计算。处理完一条射线后,就输出相交的球体数量。

代码实现

struct Sphere {
    double x, y, z; // 球心坐标
    double r; // 球半径
} sp[N];

struct Ray {
    double sx, sy, sz; // 射线起点坐标
    double ex, ey, ez; // 射线终点坐标
} r[N];

int n, m;

double get_dist(double x1, double y1, double z1, double x2, double y2, double z2) {
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2));
}

int solve(int k) {
    int cnt = 0;
    for (int i = 0; i < n; i++) {
        double dx = r[k].sx - sp[i].x, dy = r[k].sy - sp[i].y, dz = r[k].sz - sp[i].z;
        double r1 = sqrt(dx * dx + dy * dy + dz * dz); // 射线起点到球心的距离

        // 由于本题所有的操作是浮点数,为了精度和代码简洁,修改了官方题解的公式
        // 更详细的说明可以参考: https://www.luogu.com.cn/blog/Idlerine/solution-p4086
        double r2 = fabs((r[k].ex - r[k].sx) * dx + (r[k].ey - r[k].sy) * dy + (r[k].ez - r[k].sz) * dz) /
                    get_dist(r[k].sx, r[k].sy, r[k].sz, r[k].ex, r[k].ey, r[k].ez); // 射线在二维平面上的投影距离
        double r3 = sqrt(r1 * r1 - r2 * r2); // 通过勾股定理计算另一条直角边

        if (r3 <= sp[i].r + exp(-8)) { // 误差eps
            cnt++;
        }
    }
    return cnt;
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i++) {
        scanf("%lf%lf%lf%lf", &sp[i].x, &sp[i].y, &sp[i].z, &sp[i].r);
    }
    for (int i = 0; i < m; i++) {
        scanf("%lf%lf%lf%lf%lf%lf", &r[i].sx, &r[i].sy, &r[i].sz, &r[i].ex, &r[i].ey, &r[i].ez);
    }
    for (int i = 0; i < m; i++) {
        printf("%d\n", solve(i));
    }
    return 0;
}

其中,首先读入球体和射线的信息,然后逐个计算每条射线穿过的球体数量。最后输出相交的球体数量即为答案。