欢迎光临
我们一直在努力

UVa 10353 Circles in Hexagon

给定 NNN 组测试数据,每组数据包含一个浮点数 rrr,表示圆的半径。需要计算一个正六边形的最小可能边长,使得该六边形能够恰好容纳 555 个和 888 个半径为 rrr 的圆,且圆与圆之间、圆与六边形边界之间互不重叠。

输出时,对于每个 rrr,需要输出两个浮点数,分别对应能容纳 555 个圆的最小边长和能容纳 888 个圆的最小边长,结果保留小数点后 121212 位。

输入格式

第一行一个整数 NNN (0<N≤10000)(0 < N \\leq 10000)(0<N≤10000),表示测试数据组数。

接下来 NNN 行,每行一个浮点数 rrr。

输出格式

对于每组数据,输出一行两个浮点数,分别表示 555 个圆和 888 个圆对应的最小六边形边长。

样例输入

2
0.000000001
0.000000002

样例输出

0.00000000300 0.00000000371
0.00000000600 0.00000000742

题目分析

本题是一个几何优化问题,要求计算在正六边形中放置指定数量等半径圆时的最小六边形边长。由于没有简单的闭式解,我们需要通过几何分析和数值方法求解。

关键几何特性

  • 正六边形的几何参数:正六边形的边长 aaa 与其内切圆半径 RinscribedR_{inscribed}Rinscribed​ 的关系为 Rinscribed=asin⁡(60∘)=a⋅32R_{inscribed} = a \\sin(60^\\circ) = a \\cdot \\frac{\\sqrt{3}}{2}Rinscribed​=asin(60∘)=a⋅23​​。

  • 圆的排列方式:题目中已给出最佳排列方式示意图:

    • 555 个圆:三个圆沿水平方向排成一行,上下各一个圆,整体关于六边形的对称轴对称。
    • 888 个圆:三行排列,第一行和第三行各三个圆,中间一行两个圆,对称排列。
  • 约束条件:

    • 圆与圆之间相切(距离等于 2r2r2r)
    • 圆与六边形边界相切(最近距离等于 rrr)
  • 解题思路

    由于无法直接推导出边长 aaa 关于半径 rrr 的显式公式,我们采用数值方法求解:

  • 比例系数法:注意到问题具有缩放不变性,即 aaa 与 rrr 成正比。我们可以先求解 r=1r=1r=1 时的比例系数 k5k_5k5​ 和 k8k_8k8​,然后对任意 rrr 有 a5=k5⋅ra_5 = k_5 \\cdot ra5​=k5​⋅r,a8=k8⋅ra_8 = k_8 \\cdot ra8​=k8​⋅r。

  • 二分搜索:对于 r=1r=1r=1 的情况,我们通过二分搜索确定最小边长 aaa。搜索范围的下界为 2/sin⁡(60∘)2/\\sin(60^\\circ)2/sin(60∘)(至少能容纳两个圆的直径),上界为 4/sin⁡(60∘)4/\\sin(60^\\circ)4/sin(60∘)(足够大的值)。

  • 可行性判断:对于给定的边长 aaa,我们需要检查所有圆是否能放入六边形中:

    • 根据几何关系计算每个圆心的坐标
    • 验证圆与圆之间的距离是否为 2r2r2r(相切)
    • 验证最外侧的圆是否在六边形边界内(点到边界的距离 ≥r\\geq r≥r)
  • 几何计算细节:

    • 使用向量叉积计算点到直线的距离
    • 通过勾股定理计算圆心之间的水平距离
    • 六边形的右侧边界由两个顶点确定,用于判断最右侧圆的位置
  • 算法步骤

  • 预处理:计算 r=1r=1r=1 时的比例系数 k5k_5k5​ 和 k8k_8k8​:

    • 对 555 个圆的情况进行二分搜索
    • 对 888 个圆的情况进行二分搜索
    • 精度控制:使用 EPS=10−14\\texttt{EPS} = 10^{-14}EPS=10−14
  • 主循环:

    • 读取测试数据组数 NNN
    • 对于每组数据:
      • 读取半径 rrr
      • 计算 a5=k5⋅ra_5 = k_5 \\cdot ra5​=k5​⋅r
      • 计算 a8=k8⋅ra_8 = k_8 \\cdot ra8​=k8​⋅r
      • 输出结果,保留 121212 位小数
  • 时间复杂度

    • 预处理:二分搜索 O(log⁡(hi−loEPS))O(\\log(\\frac{hi-lo}{EPS}))O(log(EPShi−lo​)),常数时间
    • 主循环:O(N)O(N)O(N)
    • 总复杂度:O(N)O(N)O(N),完全满足题目要求(N≤10000N \\leq 10000N≤10000)

    空间复杂度

    • O(1)O(1)O(1),仅使用常数个变量

    代码实现

    // Circles in Hexagon
    // UVa ID: 10353
    // Verdict: Accepted
    // Submission Date: 2026-01-21
    // UVa Run Time: 0.010s
    //
    // 版权所有(C)2026,邱秋。metaphysis # yeah dot net

    #include<bits/stdc++.h>
    #define EPS 1e-14
    using namespace std;

    class Point {
    public:
    double x, y;
    Point() {}
    Point(double x, double y) : x(x), y(y) {}
    Point operator – (const Point &p) const { return Point(x – p.x, y – p.y); }
    };

    // 计算向量叉积(有向面积的两倍)
    double cross(const Point &a, const Point &b, const Point &c) {
    Point ab = b – a;
    Point ac = c – a;
    return ab.x * ac.y – ab.y * ac.x;
    }

    int main() {
    int t;
    const double PI = acos(–1.0);
    const double SIN60 = sin(PI / 3.0);
    const double COS60 = cos(PI / 3.0);

    double r = 1.0; // 固定半径计算比例系数

    // ———- 计算5个圆的比例系数 ———-
    double lo5 = (2 * r) / SIN60;
    double hi5 = (4 * r) / SIN60;

    while (hi5 – lo5 > EPS) {
    double a = (lo5 + hi5) * 0.5;
    double x1 = r / SIN60;
    double y1 = 0;
    double y2 = a * SIN60 – r;

    // 计算圆心1和圆心2之间的水平距离
    double d = 4 * r * r – (y1 – y2) * (y1 – y2);
    if (d < 0) { hi5 = a; continue; }
    double x2 = sqrt(d) + x1;

    double y3 = r;
    d = 4 * r * r – (y3 – y2) * (y3 – y2);
    double x3 = sqrt(d) + x2;

    // 检查最右侧圆是否在六边形边界内
    double area = cross(Point(a * COS60 + a, a * SIN60),
    Point(2 * a * COS60 + a, 0),
    Point(x3, y3));
    if (area >= 0) { lo5 = a; continue; }

    // 计算点到直线的距离
    double dist = –area / a;
    if (dist < r) lo5 = a;
    else hi5 = a;
    }

    // ———- 计算8个圆的比例系数 ———-
    double lo8 = (2 * r) / SIN60;
    double hi8 = (4 * r) / SIN60;

    while (hi8 – lo8 > EPS) {
    double a = (lo8 + hi8) * 0.5;
    double x1 = r / SIN60;
    double y1 = 0;
    double x2 = x1 + 2 * r * COS60;
    double y2 = 2 * r * SIN60;
    double y3 = a * SIN60 – r;

    // 计算圆心2和圆心3之间的水平距离
    double d = 4 * r * r – (y2 – y3) * (y2 – y3);
    if (d < 0) { hi8 = a; continue; }
    double x3 = sqrt(d) + x2;

    double y4 = r;
    d = 4 * r * r – (y4 – y3) * (y4 – y3);
    double x4 = sqrt(d) + x3;

    // 检查最右侧圆是否在六边形边界内
    double area = cross(Point(a * COS60 + a, a * SIN60),
    Point(2 * a * COS60 + a, 0),
    Point(x4, y4));
    if (area >= 0) { lo8 = a; continue; }

    // 计算点到直线的距离
    double dist = –area / a;
    if (dist < r) lo8 = a;
    else hi8 = a;
    }

    // ———- 处理输入输出 ———-
    scanf("%d", &t);
    while (t—) {
    scanf("%lf", &r);
    printf("%.12lf %.12lf\\n", lo5 * r, lo8 * r);
    }

    return 0;
    }

    总结

    本题通过几何分析和数值方法相结合的方式,解决了正六边形中放置等半径圆的最小边长问题。主要技巧包括:

  • 比例系数法 利用问题的缩放性质,只需计算一次比例系数。
  • 二分搜索 在合理范围内寻找最小可行解。
  • 几何计算 使用向量叉积计算点到直线的距离,确保圆的边界条件。
  • 该算法高效可靠,时间复杂度 O(N)O(N)O(N),完全满足题目要求。在实际编程竞赛中,这种几何数值方法常用于解决没有闭式解的优化问题。

    赞(0)
    未经允许不得转载:171主机测评 » UVa 10353 Circles in Hexagon
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址