CodeForces - 1C Ancient Berland Circus(计算几何)
题目大意:
给出一个正\(N\)多边形的三个点,问其最小可能面积是多少。
思路:
多边形各个点肯定都在外接圆上,并且边越多越接近外接圆的面积,因此我们考虑选取尽可能少的边数。
已知三角形ABC三点坐标,可以求出该三角形三边边长,借助海伦公式可以得到\(S_{\Delta ABC}\)。
假设有一个三角形,边长分别为\(a\),\(b\),\(c\),三角形的面积\(A\)可由以下公式求得:
$ A = \sqrt{s * (s - a) * (s - b) * (s - c)} $ ,其中 \(s = \frac{a + b + c}{2}\)。
该三角形的外接圆半径$ R = \frac{abc}{4S} $。
根据余弦定理\(cos A = \frac{b^2 + c^2 - a^2}{2bc}\)可以求出该三角形三个圆心角三角函数值,使用反三角函数\(acos()\)可以得到度数,因为圆心角的度数必为正多边形中心角的整数倍,所以求三者的\(gcd\)即可得到中心角度数\(α\)。
易得(任取其中一个非外接圆圆心的顶点做一条垂线)组成正多边形的三角形的面积为\(S_{\Delta}=\frac{R^{2}\sin \alpha}{2}\),三角形个数为\(\frac{2\pi}{\alpha }\),所以该正多边形最小面积为\(S_{min} = \frac{\pi R^2 sin \alpha}{\alpha}\)
code:
#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-4;
const double PI = acos(-1.0);
double sq(double x) { return x * x; }
double gcd(double a, double b) {
if (a < eps) return b;
if (b < eps) return a;
return gcd(b, fmod(a, b));
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
double x1, y1, x2, y2, x3, y3; cin >> x1 >> y1 >> x2 >> y2 >> x3 >> y3;
double a = sqrt(sq(x1 - x2) + sq(y1 - y2));
double b = sqrt(sq(x2 - x3) + sq(y2 - y3));
double c = sqrt(sq(x1 - x3) + sq(y1 - y3));
//海伦公式
double s = (a + b + c) / 2;
double A = sqrt(s * (s - a) * (s - b) * (s - c));
double r = a * b * c / 4.0 / A;
//计算圆心角
double Alpha = acos((2 * sq(r) - sq(a)) / 2.0 / r / r);
double Beta = acos((2 * sq(r) - sq(b)) / 2.0 / r / r);
double Gamma = 2 * PI - Alpha - Beta;
double sta = gcd(Alpha, gcd(Beta, Gamma)); //取最大公约数即为中心角
double S_min = sin(sta) * (PI / sta) * sq(r);
cout << fixed << setprecision(8) << S_min << endl;
return 0;
}
\(tips\):
- 三角形面积公式可以采用行列式形式求减少精度损失。
- \(eps\)精度取太高会导致对圆心角的要求更高,边数增多引起面积变大。
- 由于\(double\)的精度问题我们需要使用\(Gamma = 2 * PI - Alpha - Beta\)来计算最大的那个角的度数。
- \(fmod(x, y)\) 返回\(double\) \(x\) \(/\) \(double\) \(y\) 的余数