1 条题解

  • 0
    @ 2025-5-8 15:05:30

    题目分析

    本题主要是计算光线在由一系列弯曲点构成的管道中能够到达的最大 x 坐标。每个管道组件由一系列上点 [xi;yi][x_i; y_i] 描述,下点为 [xi;yi1][x_i; y_i - 1],光线从 x1x_1 处的线段光源发出,且光线在弯曲点处不弯曲、不被阻挡。若光线能到达 xnx_n,则输出 “Through all the pipe.”,否则输出能到达的最大 x 坐标,精确到小数点后两位。

    算法标签

    几何计算:本题核心在于处理光线与管道弯曲点构成的线段之间的几何关系,需要计算光线与管道线段的交点,因此涉及到几何计算的知识。枚举:需要枚举所有可能的光线传播路径,通过检查光线是否能通过管道的各个部分来确定最终能到达的最大 x 坐标。

    解题思路

    输入处理首先读取输入文件,将每个管道组件的上点坐标存储起来,同时可以根据上点坐标计算出对应的下点坐标。

    光线传播路径枚举从光源的两个端点(即起始点的上点和下点)分别发出光线,枚举所有可能的光线传播路径。对于每一条光线,检查它是否能通过管道的各个部分,即检查光线与管道的每一段是否相交。

    交点计算当光线与管道的某一段相交时,计算交点的 x 坐标。可以使用直线方程和线段方程来计算交点。

    最大 x 坐标确定在枚举所有可能的光线传播路径后,找出能到达的最大 x 坐标。如果该坐标等于 xnx_n,则输出 “Through all the pipe.”,否则输出该最大 x 坐标,精确到小数点后两位。

    代码实现步骤(c++)

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
     
    using namespace std;
    const double eps = 1e-8;
    const int inf = 0x3f3f3f3f;
    const int mx = 105;
    int n;
    double left, ans;
     
    struct Point {
        double x, y;
    }pt[mx];
     
    struct Line {
        double x1, y1, x2, y2;
        Line() {};
        Line(double _x1, double _y1, double _x2, double _y2) {
            x1 = _x1; x2 = _x2; y1 = _y1; y2 = _y2;
        }
    }up[mx], down[mx];
     
    double cross(double x1, double y1, double x2, double y2) {
        return x1*y2 - x2*y1;
    }
     
    double calc(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {
        return 1.0*((x1*y2-x2*y1)*(x3-x4) - (x3*y4-x4*y3)*(x1-x2)) / ((y2-y1)*(x3-x4) - (x2-x1)*(y3-y4));
    }
     
    bool judge(double x1, double y1, double x2, double y2) {
        if (cross(pt[2].x-x1, pt[2].y-y1, x2-x1, y2-y1)*
            cross(pt[1].x-x1, pt[1].y-y1, x2-x1, y2-y1) > eps)
                return false;
        for (int i = 2; i <= n; i++) {
            if(cross(x1-up[i].x1, y1-up[i].y1, x2-up[i].x1, y2-up[i].y1) > -eps
               && cross(x1-up[i].x2, y1-up[i].y2, x2-up[i].x2, y2-up[i].y2) > -eps)
               continue;
     
            double tmp = calc(up[i].x1, up[i].y1, up[i].x2, up[i].y2, x1, y1, x2, y2);
            left = min(left, tmp);
            break;
        }
        for (int i = 2; i <= n; i++) {
            if(cross(x1-down[i].x1, y1-down[i].y1, x2-down[i].x1, y2-down[i].y1) < eps
               && cross(x1-down[i].x2, y1-down[i].y2, x2-down[i].x2, y2-down[i].y2) < eps)
               continue;
     
            double tmp = calc(down[i].x1, down[i].y1, down[i].x2, down[i].y2, x1, y1, x2, y2);
            left = min(left, tmp);
            break;
        }
        return true;
    }
     
    void solve() {
        ans = -inf;
        for (int i = 1; i <= 2*n; i++) {
            for (int j = i+1; j <= 2*n; j++) {
                left = inf;
                if (pt[i].x == pt[j].x) continue;
                if (judge(pt[i].x, pt[i].y, pt[j].x, pt[j].y))
                    ans = max(ans, left);
            }
        }
    }
     
    int main() {
        while (scanf("%d", &n) != EOF && n != 0) {
            for (int i = 1; i <= n; i++) {
                scanf("%lf %lf", &pt[i*2].x, &pt[i*2].y);
                pt[i*2-1].x = pt[i*2].x; pt[i*2-1].y = pt[i*2].y - 1;
     
                if (i >= 2) {
                    up[i] = Line(pt[i*2-2].x, pt[i*2-2].y, pt[i*2].x, pt[i*2].y);
                    down[i] = Line(pt[i*2-3].x, pt[i*2-3].y, pt[i*2-1].x, pt[i*2-1].y);
                }
            }
            solve();
            if (inf - ans < eps) puts("Through all the pipe.");
            else printf("%.2f\n", ans);
        }
     
        return 0;
    }
    
    • 1

    信息

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