题目描述
一家工厂要生产一种弹珠滑梯玩具。该玩具由两根垂直的木杆和一些小的刚性挡板组成。挡板交替连接在左右木杆上:奇数挡板连接在左杆,偶数挡板连接在右杆。一颗小弹珠从最高处的挡板落下,在重力作用下沿着挡板滑动,最终离开玩具。
工厂主已经完成了玩具的设计,包括每个挡板的尺寸、位置和倾斜角度。成千上万个玩具正在中国生产,工厂经理被要求购买弹珠。然而,在订购成千上万颗弹珠之前,他想知道弹珠的最大直径,以确保弹珠不会卡在玩具中间。
工厂经理希望你能编写一个程序,根据玩具规格,确定弹珠能到达玩具末端而不被卡住的最大直径。
输入格式
输入包含多个测试用例。每个测试用例的第一行包含一个整数 NNN ,表示玩具的挡板数量。第二行包含两个整数 LLL 和 HHH ,分别表示两根木杆之间的距离和木杆的高度。左侧木杆位于 XXX 轴的位置 000 ,因此右侧木杆位于 XXX 轴的位置 LLL 。
接下来的 NNN 行描述每个挡板。挡板从高到低描述,交替描述它们所连接的木杆。最高的挡板(第一个被描述的)一端连接到左侧木杆,第二高的挡板(第二个被描述的)一端连接到右侧木杆,依此类推。奇数挡板连接到左侧木杆,偶数挡板连接到右侧木杆。
每行描述一个挡板,包含三个整数 YiY_iYi 、 XfX_fXf 和 YfY_fYf 。 (Xf,Yf)(X_f, Y_f)(Xf,Yf) 表示挡板自由端(未连接端)的坐标。奇数挡板连接端的坐标为 (0,Yi)(0, Y_i)(0,Yi) ,偶数挡板连接端的坐标为 (L,Yi)(L, Y_i)(L,Yi) 。
对于所有挡板, Yi>YfY_i > Y_fYi>Yf (即挡板起点和终点之间存在斜率),且挡板的长度小于玩具的宽度。此外,对于两个连续的挡板 AAA 和 BBB , YfA≥YiBY_{fA} \geq Y_{iB}YfA≥YiB (即挡板 AAA 的终点高度大于等于挡板 BBB 的起点高度)。假设挡板非常薄,其厚度可以忽略不计。
输出格式
对于每个测试用例,输出一行,包含一个精确到两位小数的数字,表示弹珠能够到达玩具末端而不卡住的最大直径。
限制条件
- 1≤N≤1031 \leq N \leq 10^31≤N≤103
- 1≤L≤1031 \leq L \leq 10^31≤L≤103
- 1≤H≤1031 \leq H \leq 10^31≤H≤103
- 0<Xf<L0 < X_f < L0<Xf<L
- 0≤Yi≤H, 0≤Yf≤H0 \leq Y_i \leq H, \, 0 \leq Y_f \leq H0≤Yi≤H,0≤Yf≤H 且 Yi>YfY_i > Y_fYi>Yf
样例输入
3
6 10
9 3 8
6 2 5
4 3 1
3
5 10
9 3 7
7 2 4
2 3 0
样例输出
2.00
1.41
题目分析与解题思路
问题分析
弹珠在滑梯中运动时可能被卡住的两种情况:
-
弹珠与侧壁之间的空隙 :弹珠在挡板上滚动时,不能撞到对侧的木杆。因此,弹珠直径必须小于等于挡板自由端到对侧木杆的水平距离。
-
相邻挡板之间的过渡 :弹珠从挡板 AAA 的自由端落到挡板 BBB 上。最狭窄的地方是挡板 AAA 的自由端(一个点)到挡板 BBB 整个线段的最短距离。这是因为弹珠要落到挡板 BBB 上,不能撞到挡板 BBB 的侧面或上表面。
数学模型
1. 自由端到对侧木杆的距离
对于每个挡板 iii (从 000 开始编号):
- 如果 iii 是偶数(对应题目中的奇数挡板,连接在左杆):自由端在右侧,距离 = L−XfL - X_fL−Xf
- 如果 iii 是奇数(对应题目中的偶数挡板,连接在右杆):自由端在左侧,距离 = XfX_fXf
弹珠的最大直径必须小于等于这个距离。
2. 点到线段的最短距离
对于相邻挡板 iii 和 i+1i+1i+1 :
- 点 PPP = 挡板 iii 的自由端 (Xfi,Yfi)(X_{f_i}, Y_{f_i})(Xfi,Yfi)
- 线段 SSS = 挡板 i+1i+1i+1 ,从固定端到自由端
- 固定端:如果 i+1i+1i+1 是偶数,为 (0,Yi+1)(0, Y_{i+1})(0,Yi+1) ;如果 i+1i+1i+1 是奇数,为 (L,Yi+1)(L, Y_{i+1})(L,Yi+1)– 自由端:(Xfi+1,Yfi+1)(X_{f_{i+1}}, Y_{f_{i+1}})(Xfi+1,Yfi+1)
我们需要计算点 PPP 到线段 SSS 的最短距离。这是一个标准的几何问题,可以通过以下步骤计算:
- 计算线段的方向向量 v⃗=(x2−x1,y2−y1)\vec{v} = (x_2 - x_1, y_2 - y_1)v=(x2−x1,y2−y1)
- 如果线段退化为点(方向向量长度为 000 ),则直接计算两点距离
- 计算点 PPP 到线段所在直线的投影参数 ttt :
t=(Px−x1)(x2−x1)+(Py−y1)(y2−y1)(x2−x1)2+(y2−y1)2 t = \frac{(P_x - x_1)(x_2 - x_1) + (P_y - y_1)(y_2 - y_1)}{(x_2 - x_1)^2 + (y_2 - y_1)^2} t=(x2−x1)2+(y2−y1)2(Px−x1)(x2−x1)+(Py−y1)(y2−y1) - 限制 ttt 在 [0,1][0, 1][0,1] 范围内:
- 如果 t<0t < 0t<0 ,则最近点是线段起点
- 如果 t>1t > 1t>1 ,则最近点是线段终点
- 否则,最近点在线段上
- 计算最近点坐标,然后计算点 PPP 到该点的距离
算法步骤
- 读取输入数据,存储每个挡板的固定端坐标和自由端坐标
- 初始化最大直径 minDiameterminDiameterminDiameter 为一个很大的值
- 对于每个挡板,计算自由端到对侧木杆的距离,更新 minDiameterminDiameterminDiameter
- 对于每对相邻挡板,计算前一个挡板自由端到后一个挡板线段的最短距离,更新 minDiameterminDiameterminDiameter
- 输出 minDiameterminDiameterminDiameter ,保留两位小数
复杂度分析
- 时间复杂度: O(N)O(N)O(N) ,其中 NNN 是挡板数量。我们需要遍历每个挡板一次,以及每对相邻挡板一次
- 空间复杂度: O(N)O(N)O(N) ,用于存储挡板信息
代码实现
// Toboggan of Marbles
// UVa ID: 12483
// Verdict: Accepted
// Submission Date: 2025-12-04
// UVa Run Time: 0.010s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
// 点结构体
struct Point {
double x, y;
Point(double x = 0, double y = 0) : x(x), y(y) {}
};
// 挡板结构体
struct Flap {
int yi, xf, yf;
int connX; // 固定端的x坐标
};
// 计算点p到线段ab的最短距离
double pointToSegmentDistance(Point p, Point a, Point b) {
double dx = b.x - a.x;
double dy = b.y - a.y;
// 如果线段退化为点
if (fabs(dx) < 1e-9 && fabs(dy) < 1e-9)
return sqrt((p.x - a.x) * (p.x - a.x) + (p.y - a.y) * (p.y - a.y));
// 计算投影参数t
double t = ((p.x - a.x) * dx + (p.y - a.y) * dy) / (dx * dx + dy * dy);
// 限制t在[0,1]范围内
if (t < 0.0) t = 0.0;
if (t > 1.0) t = 1.0;
// 计算投影点坐标
Point proj(a.x + t * dx, a.y + t * dy);
// 返回距离
return sqrt((p.x - proj.x) * (p.x - proj.x) + (p.y - proj.y) * (p.y - proj.y));
}
int main() {
int n;
while (cin >> n) {
int L, H;
cin >> L >> H;
vector<Flap> flaps(n);
for (int i = 0; i < n; i++) {
cin >> flaps[i].yi >> flaps[i].xf >> flaps[i].yf;
// 奇数挡板(0-based)连左杆(0),偶数挡板连右杆(L)
flaps[i].connX = (i % 2 == 0) ? 0 : L;
}
double minDiameter = 1e9;
// 1. 检查自由端到对侧木杆的距离
for (int i = 0; i < n; i++) {
double distance;
if (i % 2 == 0) { // 奇数挡板,连左杆
distance = L - flaps[i].xf; // 到右杆的距离
} else { // 偶数挡板,连右杆
distance = flaps[i].xf; // 到左杆的距离
}
minDiameter = min(minDiameter, distance);
}
// 2. 检查相邻挡板之间的过渡
for (int i = 0; i < n - 1; i++) {
// 挡板i的自由端
Point p(flaps[i].xf, flaps[i].yf);
// 挡板i+1的线段:从固定端到自由端
Point a(flaps[i+1].connX, flaps[i+1].yi); // 固定端
Point b(flaps[i+1].xf, flaps[i+1].yf); // 自由端
double distance = pointToSegmentDistance(p, a, b);
minDiameter = min(minDiameter, distance);
}
cout << fixed << setprecision(2) << minDiameter << endl;
}
return 0;
}
总结
本题的关键在于识别弹珠可能被卡住的两种几何约束:弹珠与侧壁之间的空隙,以及相邻挡板之间的过渡。通过计算点到线段的距离和点到垂直线的距离,我们可以确定弹珠能够通过的最大直径。这是一个典型的计算几何问题,需要仔细处理几何计算和精度问题。
2877

被折叠的 条评论
为什么被折叠?



