题目分析
本题要求我们根据正多边形的三个顶点坐标,确定这个正多边形可能的最小顶点数 n n n。题目保证这三个顶点来自同一个正多边形,且该多边形的顶点数不超过 200 200 200。
正多边形的基本性质
正多边形的所有顶点都位于其外接圆上,并且均匀分布。这意味着相邻顶点之间的圆心角相等,均为 2 π n \frac{2\pi}{n} n2π,其中 n n n 是多边形的顶点数。
关键观察
给定三个顶点 A , B , C A, B, C A,B,C 在正多边形的外接圆上,它们将圆周分成三段弧。每段弧对应的圆心角都应该是 2 π n \frac{2\pi}{n} n2π 的整数倍。换句话说,如果我们计算三个顶点两两之间的圆心角差(取小于 π \pi π 的值,因为多边形顶点间的最大弧对应角不超过 π \pi π),那么这些角度差都应该满足:
角度差 = k ⋅ 2 π n \text{角度差} = k \cdot \frac{2\pi}{n} 角度差=k⋅n2π
其中 k k k 是正整数。
解题思路
-
计算外接圆圆心:首先需要找到三个点所在的外接圆圆心,这可以通过计算三角形的外心得到。
-
计算极角:将三个点转换为相对于圆心的极坐标角度。
-
排序并计算角度差:对极角进行排序,计算相邻点之间的角度差(包括首尾相连的情况),并确保每个角度差不超过 π \pi π。
-
枚举验证:枚举可能的顶点数 n n n(从 3 3 3 到 200 200 200),检查是否所有角度差都是 2 π n \frac{2\pi}{n} n2π 的整数倍(在一定误差范围内)。
-
输出最小解:找到满足条件的最小 n n n 并输出。
算法细节
- 外心计算:使用三角形外心的坐标公式计算外接圆圆心。
- 极角计算:使用
atan2函数计算点相对于圆心的角度。 - 角度归一化:确保所有角度在 [ 0 , 2 π ) [0, 2\pi) [0,2π) 范围内。
- 浮点精度处理:由于浮点数计算存在精度问题,使用一个小的容差值(如 1 0 − 4 10^{-4} 10−4)来判断角度是否为 2 π n \frac{2\pi}{n} n2π 的整数倍。
代码实现
// Temple of Dune
// UVa ID: 10439
// Verdict: Accepted
// Submission Date: 2025-11-05
// UVa Run Time: 0.000s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <iostream>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;
const double PI = acos(-1.0); // 定义圆周率
const double EPS = 1e-4; // 浮点数比较容差
// 点结构体
struct Point {
double x, y;
Point(double x = 0, double y = 0) : x(x), y(y) {}
};
// 计算三角形外心(外接圆圆心)
Point circumcenter(Point A, Point B, Point C) {
double D = 2 * (A.x * (B.y - C.y) + B.x * (C.y - A.y) + C.x * (A.y - B.y));
double Ux = ((A.x * A.x + A.y * A.y) * (B.y - C.y) +
(B.x * B.x + B.y * B.y) * (C.y - A.y) +
(C.x * C.x + C.y * C.y) * (A.y - B.y)) / D;
double Uy = ((A.x * A.x + A.y * A.y) * (C.x - B.x) +
(B.x * B.x + B.y * B.y) * (A.x - C.x) +
(C.x * C.x + C.y * C.y) * (B.x - A.x)) / D;
return Point(Ux, Uy);
}
// 计算点的极角
double angle(Point p) {
return atan2(p.y, p.x);
}
// 角度归一化到 [0, 2π) 范围
double normalize(double a) {
while (a < 0) a += 2 * PI;
while (a >= 2 * PI) a -= 2 * PI;
return a;
}
int main() {
int nCases;
cin >> nCases;
while (nCases--) {
Point p[3];
// 读入三个点的坐标
for (int i = 0; i < 3; i++) {
cin >> p[i].x >> p[i].y;
}
// 计算外接圆圆心
Point center = circumcenter(p[0], p[1], p[2]);
// 计算三个点相对于圆心的极角
vector<double> angles;
for (int i = 0; i < 3; i++) {
double dx = p[i].x - center.x;
double dy = p[i].y - center.y;
angles.push_back(atan2(dy, dx));
}
// 对极角排序
sort(angles.begin(), angles.end());
// 计算相邻点之间的角度差(取小于 π 的值)
vector<double> diffs;
for (int i = 0; i < 3; i++) {
double diff = angles[(i + 1) % 3] - angles[i];
if (diff < 0) diff += 2 * PI; // 处理负角度差
if (diff > PI) diff = 2 * PI - diff; // 取较小的弧
diffs.push_back(diff);
}
// 枚举可能的顶点数 n,从 3 到 200
int ans = 200; // 初始化为最大值
for (int n = 3; n <= 200; n++) {
double unit = 2 * PI / n; // 单位角度
bool ok = true;
// 检查每个角度差是否是单位角度的整数倍
for (double d : diffs) {
double k = d / unit; // 计算倍数
// 检查 k 是否接近整数(考虑浮点误差)
double r = fabs(k - round(k));
if (r > EPS && fabs(k - round(k) - 1) > EPS && fabs(k - round(k) + 1) > EPS) {
ok = false;
break;
}
}
// 如果满足条件,更新答案并退出循环
if (ok) {
ans = n;
break;
}
}
cout << ans << endl;
}
return 0;
}
复杂度分析
- 时间复杂度:对于每个测试用例,需要枚举 198 198 198 个可能的 n n n 值(从 3 3 3 到 200 200 200),每次枚举进行常数次浮点运算。因此总时间复杂度为 O ( m × 198 ) O(m \times 198) O(m×198),其中 m m m 是测试用例的数量。
- 空间复杂度: O ( 1 ) O(1) O(1),只使用了固定大小的变量和数组。
总结
本题的关键在于利用正多边形的几何性质,通过计算三个顶点在外接圆上的角度关系来确定可能的顶点数。算法通过枚举所有可能的顶点数并验证几何约束,最终找到满足条件的最小解。浮点数精度的处理是本题实现中的一个重要细节。
344

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



