📅  最后修改于: 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;
}
其中,首先读入球体和射线的信息,然后逐个计算每条射线穿过的球体数量。最后输出相交的球体数量即为答案。