1 条题解

  • 0
    @ 2025-5-8 20:25:20

    题意分析

    本题中斯坦有 nn 根长度不同的棍子,以随机方式扔到地板上,要找出扔完后处于最上面(即上面没有其他棍子)的棍子。输入每个案例时,先输入棍子数量 nn1n1000001 \leq n \leq 100000),接着 nn 行每行四个数字,表示一根棍子两个端点的平面坐标,且棍子按扔的顺序给出。需按扔的顺序输出处于最上面的棍子编号。

    解题思路

    1. 数据存储与定义:定义结构体 PointPoint 存储点的坐标,同时定义 VectorVector(实际就是 PointPoint,用于向量运算),重载减法运算符用于计算向量,定义 CrossCross 函数计算向量叉积。定义数组 dtdt 存储棍子的端点坐标,长度为 2n2n(因为每根棍子两个端点)。
    2. 输入与初始化:循环读取每个案例的 nn,并依次读取每根棍子两个端点的坐标存储到 dtdt 数组中。初始化变量 kk 为 0(不过在当前代码中 kk 未实际使用)。
    3. 判断最上面的棍子:外层循环遍历每根棍子 ii(从 1 到 nn)。内层循环遍历其余棍子 jj(从 i+1i + 1nn)。通过向量叉积判断棍子 ii 和棍子 jj 的位置关系,若棍子 jj 的两个端点相对于棍子 ii 的两个端点的向量叉积都满足一定条件(即棍子 jj 的端点在棍子 ii 端点所确定直线的同一侧),则说明棍子 jj 不在棍子 ii 上面。当内层循环完整执行完(即没有其他棍子在棍子 ii 上面),则棍子 ii 是处于最上面的棍子,按格式输出其编号。最后输出最后一根棍子的编号(因为最后扔的棍子肯定在最上面)。

    代码解释

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    
    const int N = 200010;
    // 定义点的结构体
    struct Point {
        double x, y;
        Point(double x = 0, double y = 0) : x(x), y(y) {}
    };
    
    // 定义向量(实际就是点,用于向量运算)
    typedef Point Vector;
    
    Point dt[N];
    // 重载减法运算符,用于计算向量
    Point operator-(Point a, Point b) { return Vector(a.x - b.x, a.y - b.y); }
    // 计算向量叉积的函数
    double Cross(Vector a, Vector b) { return a.x * b.y - a.y * b.x; }
    
    int main() {
        int n, k, j;
        // 循环读取每个案例的 n,当 n 为 0 时结束输入
        while (scanf("%d", &n), n) {
            k = 0;
            // 读取每根棍子两个端点的坐标存储到 dt 数组中
            for (int i = 1; i <= n; i++)
                scanf("%lf%lf%lf%lf", &dt[i].x, &dt[i].y, &dt[i + n].x, &dt[i + n].y);
            // 输出提示信息
            printf("Top sticks: ");
            // 遍历每根棍子 i
            for (int i = 1; i < n; i++) {
                // 遍历其余棍子 j
                for (j = i + 1; j <= n; j++) {
                    // 通过向量叉积判断棍子 i 和棍子 j 的位置关系
                    if (Cross(dt[i] - dt[j], dt[i] - dt[j + n]) * Cross(dt[i + n] - dt[j], dt[i + n] - dt[j + n]) <= 0)
                        if (Cross(dt[j] - dt[i], dt[j] - dt[i + n]) * Cross(dt[j + n] - dt[i], dt[j + n] - dt[i + n]) <= 0)
                            break;
                }
                // 如果内层循环完整执行完,说明棍子 i 是处于最上面的棍子,输出其编号
                if (j == n + 1)printf("%d, ", i);
            }
            // 输出最后一根棍子的编号(最后扔的棍子肯定在最上面)
            printf("%d.\n", n);
        }
        return 0;
    }
    
    1. 结构体与函数定义部分:定义 PointPoint 结构体表示点的坐标,VectorVector 类型实际就是 PointPoint,重载减法运算符方便计算向量,CrossCross 函数用于计算向量叉积,这些都是为后续判断棍子位置关系做准备。
    2. 主函数部分:通过 whilewhile 循环读取每个案例的 nn 及棍子端点坐标。两层循环用于遍历棍子并判断位置关系,根据判断结果输出处于最上面的棍子编号。

    复杂度分析

    1. 时间复杂度:输入数据部分时间复杂度为 O(n)O(n)(读取 nn 根棍子的端点坐标)。判断最上面棍子的部分,外层循环 n1n - 1 次,内层循环平均 nin - i 次(ii 从 1 到 n1n - 1),每次循环内的向量叉积计算是常数时间,所以这部分时间复杂度为 O(n2)O(n^2)。整体时间复杂度为 O(n2)O(n^2)
    2. 空间复杂度:存储棍子端点坐标使用数组 dtdt,长度为 2n2n,所以空间复杂度为 O(n)O(n)

    注意事项

    1. 输入数据验证:代码中未对输入的坐标数据进行有效性验证,如坐标值的范围等,实际应用中可能需要添加验证逻辑。
    2. 浮点数运算:由于使用了浮点数进行向量叉积计算,可能会存在精度问题,对于一些边界情况(如非常接近平行的棍子),可能会影响判断结果。
    • 1

    信息

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