UVa 12395 Regular Convex Polygon

题目描述

一个正凸多边形 是指所有边等长、所有内角相等且小于 180180180 度的多边形。例如,正方形就是一个正凸多边形。给定三个点,它们是某个正凸多边形 RRR 的顶点;请你确定 RRR 至少 有多少个顶点。

输入格式

每个测试用例由三行组成。第 iii 行包含两个浮点数值 xix_ixiyiy_iyi−104≤x1,y1≤104-10^4 \leq x_1, y_1 \leq 10^4104x1,y1104 ),其中 (xi,yi)(x_i, y_i)(xi,yi)RRR 的一个顶点的坐标。坐标的精度为 10−610^{-6}106 ,即它们与精确坐标的误差不超过 10−610^{-6}106 。可以假设每个测试用例中任意两个给定点之间的欧几里得距离至少为 111 ,且 RRR 最多有 100010001000 个顶点。输入以包含单词 END 的一行结束。

输出格式

对于每个测试用例,输出一行,表示 RRR 必须具有的最小顶点数。

样例输入

-1385.736326 -146.954822
430.000292 -2041.361203
1162.736034 478.316025
0.000000 4147.000000
-4147.000000 0.000000
0.000000 -4147.000000
END

样例输出

3
4

题目分析

本题要求根据正凸多边形的三个已知顶点,确定这个多边形至少有多少个顶点。这是一个几何推理与计算几何相结合的问题。

关键观察

  1. 正多边形的性质 :正凸多边形的所有顶点均匀分布在其外接圆上。
  2. 已知三个顶点 :这三个顶点是该正多边形的一部分,它们在外接圆上形成三个弧段。
  3. 圆心角关系 :设正多边形有 nnn 个顶点,则每相邻两个顶点与圆心连线所夹的圆心角为 360∘n\frac{360^\circ}{n}n360 (或 2πn\frac{2\pi}{n}n2π 弧度)。
  4. 已知顶点间的圆心角 :已知的三个顶点 AAABBBCCC 与圆心 OOO 形成的三个圆心角 ∠AOB\angle AOBAOB∠BOC\angle BOCBOC∠COA\angle COACOA 都应该是 360∘n\frac{360^\circ}{n}n360 的整数倍,因为这些顶点在圆上是等距分布的。
  5. 最小 nnn 的确定 :我们需要找到最小的整数 nnnn≥3n \ge 3n3 ),使得上述三个圆心角都是 360∘n\frac{360^\circ}{n}n360 的整数倍。

解题思路

基于以上观察,我们可以按以下步骤解决问题:

  1. 计算外接圆圆心
    由于三个点在一个正多边形的外接圆上,我们可以通过这三个点计算三角形的外接圆圆心。这可以通过解垂直平分线的交点来实现,但更直接的方法是使用三角形外心公式。

  2. 计算圆心角
    对于每对已知顶点( AAABBBBBBCCCCCCAAA ),计算它们与圆心 OOO 形成的圆心角 ∠AOB\angle AOBAOB 等。为了减少精度误差,我们使用余弦定理计算角度,并且使用角度制而非弧度制,因为角度制的数值范围更适合进行取整比较。

  3. 枚举可能的 nnn
    由于题目保证 n≤1000n \le 1000n1000 ,我们可以从 n=3n = 3n=3 开始枚举到 n=1000n = 1000n=1000 。对于每个 nnn ,计算单位圆心角 unit=360n\text{unit} = \frac{360}{n}unit=n360 ,然后检查三个圆心角是否都是 unit\text{unit}unit 的整数倍(在一定的误差范围内)。

  4. 处理浮点误差
    由于输入坐标和计算过程中存在浮点误差,我们不能直接进行精确的等值比较。需要设置一个合理的误差容忍值 EPS\text{EPS}EPS 。经过测试, EPS=10−5\text{EPS} = 10^{-5}EPS=105 是一个合适的选择,既能保证正确性,又不会因过于宽松而接受错误答案。

  5. 输出最小 nnn
    一旦找到满足条件的 nnn ,就立即输出,因为我们是按从小到大的顺序枚举的,所以第一个满足条件的 nnn 就是最小的。

算法复杂度

  • 对于每个测试用例,需要枚举 nnn333100010001000 ,最多 998998998 次。
  • 每次枚举需要检查 333 个角度,常数时间。
  • 因此总时间复杂度为 O(1000×3)=O(3000)O(1000 \times 3) = O(3000)O(1000×3)=O(3000) 每个测试用例,完全在可接受范围内。

注意事项

  • 计算外心时需要注意三点共线的情况,但根据正多边形的定义(凸多边形,内角小于 180∘180^\circ180 ),三点不会共线。
  • 使用余弦定理时,需要确保 cos⁡θ\cos \thetacosθ 的值在 [−1,1][-1, 1][1,1] 范围内,避免浮点误差导致计算结果超出定义域。
  • 角度取整时使用 round 函数,然后比较差值是否在 EPS\text{EPS}EPS 内。

代码实现

// Regular Convex Polygon
// UVa ID: 12395
// Verdict: Accepted
// Submission Date: 2025-12-26
// UVa Run Time: 0.000s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net

#include <bits/stdc++.h>
using namespace std;

const double EPS = 1e-5;  // 经过测试,1e-5是最合适的精度要求
const double PI = acos(-1.0);

struct Point {
    double x, y;
    Point(double x = 0, double y = 0) : x(x), y(y) {}
    Point operator + (const Point& p) const { return Point(x + p.x, y + p.y); }
    Point operator - (const Point& p) const { return Point(x - p.x, y - p.y); }
};

// 计算三角形外接圆圆心
Point circumcenter(const Point& a, const Point& b, const Point& c) {
    double a1 = b.x - a.x, b1 = b.y - a.y, c1 = (a1 * a1 + b1 * b1) / 2;
    double a2 = c.x - a.x, b2 = c.y - a.y, c2 = (a2 * a2 + b2 * b2) / 2;
    double d = a1 * b2 - a2 * b1;
    return Point(a.x + (c1 * b2 - c2 * b1) / d, a.y + (a1 * c2 - a2 * c1) / d);
}

// 计算两点距离
double distance(const Point& a, const Point& b) {
    double dx = a.x - b.x, dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
}

// 使用余弦定理计算圆心角(度)
double computeAngle(const Point& a, const Point& b, const Point& center) {
    double oa = distance(a, center);
    double ob = distance(b, center);
    double ab = distance(a, b);
    
    // 余弦定理:cosθ = (oa² + ob² - ab²) / (2·oa·ob)
    double cosTheta = (oa * oa + ob * ob - ab * ab) / (2 * oa * ob);
    
    // 防止浮点误差
    if (cosTheta > 1.0) cosTheta = 1.0;
    else if (cosTheta < -1.0) cosTheta = -1.0;
    
    return acos(cosTheta) * 180.0 / PI;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    while (true) {
        vector<Point> points(3);
        for (int i = 0; i < 3; ++i) {
            if (!(cin >> points[i].x >> points[i].y)) {
                string endCheck;
                cin.clear();
                cin >> endCheck;
                return 0;
            }
        }
        
        // 计算外接圆圆心
        Point center = circumcenter(points[0], points[1], points[2]);
        
        // 计算三个圆心角(度)
        vector<double> angles(3);
        angles[0] = computeAngle(points[0], points[1], center);
        angles[1] = computeAngle(points[1], points[2], center);
        angles[2] = computeAngle(points[2], points[0], center);
        
        // 枚举边数n,找到最小的满足条件的n
        int answer = 1000;
        for (int n = 3; n <= 1000; ++n) {
            double unitAngle = 360.0 / n;  // 正n边形每个圆心角(度)
            bool valid = true;
            
            for (double angle : angles) {
                double k = angle / unitAngle;
                double rounded = round(k);
                if (fabs(k - rounded) > EPS) {
                    valid = false;
                    break;
                }
            }
            
            if (valid) {
                answer = n;
                break;  // 找到最小的n就退出
            }
        }
        
        cout << answer << "\n";
    }
    
    return 0;
}

总结

本题是一个典型的计算几何问题,结合了正多边形的几何性质和浮点数精度处理。关键点在于认识到三个已知顶点均匀分布在外接圆上,从而通过计算圆心角并检查它们是否为单位圆心角的整数倍来确定最小顶点数。使用角度制和合适的误差容忍值是保证算法正确的关键。通过枚举 nnn 并利用题目给出的 n≤1000n \le 1000n1000 的条件,可以在合理时间内解决问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值