1 条题解

  • 0
    @ 2025-6-10 21:07:18

    题意分析

    题目要求计算激光在多个反射球体间传播后的最后一个反射点。激光从原点(0,0,0)(0,0,0)沿方向(u,v,w)(u,v,w)射出,每次反射遵循入射角等于反射角的规则。需模拟光线在球体间的反射过程,直到光线不再与任何球体相交。每个球体由球心坐标(xi,yi,zi)(x_i,y_i,z_i)和半径rir_i定义,且任意两球表面间距≥0.1,原点距任何球体表面≥0.1。

    解题思路

    代码通过向量运算和几何关系模拟反射过程:1)定义三维向量类PP处理点和方向;2)每次计算光线与所有球体的交点,选择最近的有效交点作为反射点;3)通过对称变换计算反射后的方向向量;4)重复上述过程直至光线不再与任何球体相交。需注意:1)使用误差控制避免浮点精度问题;2)反射点必须位于光线前进方向;3)输出最后反射点坐标,保留4位小数。

    #include<cstdio> 
    #include<cstring> 
    #include<cmath> 
    #include<vector> 
    #include<set> 
    #include<map> 
    #include<algorithm> 
    
    const int maxn = 1e2;
    const double pi = acos(-1.0);
    const double eps = 1e-5;
    
    using namespace std;
    
    // 加法误差 
    double add(double a, double b) {
        if (fabs(a + b) < eps)
            return 0;
        return a + b;
    }
    
    // 向量运算 
    struct P {
        double x, y, z;
        
        P() {}
        P(double x, double y, double z) : x(x), y(y), z(z) {}
        
        P operator + (P p) {
            return P(add(x, p.x), add(y, p.y), add(z, p.z));
        }
        
        P operator - (P p) {
            return P(add(x, -p.x), add(y, -p.y), add(z, -p.z));
        }
        
        P operator * (double d) {
            return P(x * d, y * d, z * d);
        }
        
        // 点乘 
        double dot(P p) {
            return add(add(x * p.x, y * p.y), z * p.z);
        }
        
        // 面积 
        double area(P p) {
            double dx = add(y * p.z, -z * p.y);
            double dy = -add(x * p.z, -z * p.x);
            double dz = add(x * p.y, -y * p.x);
            double s = add(add(dx * dx, dy * dy), dz * dz);
            return sqrt(s);
        }
        
        // 向量模长 
        double lenth() {
            return sqrt(add(add(x * x, y * y), z * z));
        }
        
        // 单位向量 
        P unit() {
            return P(x, y, z) * (1 / lenth());
        }
        
        // 向量p1-p在向量p1-p2上的投影模长 
        double prj_len(P p1, P p2) {
            P p(x, y, z);
            return (p - p1).dot((p2 - p1).unit());
        }
        
        // 在p1-p2上的投影(垂足) 
        P prj(P p1, P p2) {
            P p(x, y, z);
            return p1 + (p2 - p1).unit() * p.prj_len(p1, p2);
        }
        
        // 关于p1-p2的对称点 
        P sym(P p1, P p2) {
            P p(x, y, z);
            P e = p.prj(p1, p2);
            return e * 2 - p;
        }
    };
    
    struct dis {
        P p;
        double d;
        int ind;
        
        dis() {}
        dis(P p, double d, int ind) : p(p), d(d), ind(ind) {}
    };
    
    bool operator < (dis a, dis b) {
        return a.d < b.d;
    }
    
    int main() {
        int n, num;
        double r[maxn];
        P st, a[maxn], s;
        dis df[maxn];
        
        while (scanf("%d", &n) && n) {
            int m = -1;
            scanf("%lf %lf %lf", &s.x, &s.y, &s.z);
            st.x = st.y = st.z = 0;
            
            for (int i = 0; i < n; i++) {
                scanf("%lf %lf %lf %lf", &a[i].x, &a[i].y, &a[i].z, &r[i]);
            }
            
            while (1) {
                num = 0;
                P next = st + s, toy;
                
                for (int i = 0; i < n; i++) {
                    if (i == m)
                        continue;
                    
                    P e = a[i].prj(st, next);
                    P ss = e - a[i];
                    double d = ss.lenth();
                    
                    if (d > r[i] || !add(r[i], -d))
                        continue; // 相切或相离
                    
                    double dx = s.x, dy = s.y, dz = s.z;
                    double Dx = add(st.x, -a[i].x), Dy = add(st.y, -a[i].y), Dz = add(st.z, -a[i].z);
                    
                    // A*t^2 + B*t + C = 0 
                    double A = add(add(dx * dx, dy * dy), dz * dz);
                    double B = 2 * add(add(dx * Dx, dy * Dy), dz * Dz);
                    double C = add(add(Dx * Dx, Dy * Dy), add(Dz * Dz, -r[i] * r[i]));
                    double lf = add(B * B, -4 * A * C);
                    
                    if (lf <= 0)
                        continue;
                    
                    double dd = sqrt(lf);
                    double t1 = add(-B, dd) / 2 / A, t2 = add(-B, -dd) / 2 / A;
                    P p1 = P(add(t1 * dx, st.x), add(t1 * dy, st.y), add(t1 * dz, st.z));
                    double d1 = (p1 - st).lenth();
                    P p2 = P(add(t2 * dx, st.x), add(t2 * dy, st.y), add(t2 * dz, st.z));
                    double d2 = (p2 - st).lenth();
                    
                    if (s.dot(p1 - st) > 0)
                        df[num++] = dis(p1, d1, i);
                    if (s.dot(p2 - st) > 0)
                        df[num++] = dis(p2, d2, i);
                }
                
                if (!num)
                    break;
                
                sort(df, df + num);
                m = df[0].ind;
                P e = st.sym(a[df[0].ind], df[0].p);
                st = df[0].p;
                s = e - st;
                P p2 = e;
            }
            
            printf("%.4lf %.4lf %.4lf\n", st.x, st.y, st.z);
        }
        
        return 0;
    }
    
    • 1

    信息

    ID
    2935
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者