1. 程式人生 > 實用技巧 >CodeForces - 1C Ancient Berland Circus(計算幾何)

CodeForces - 1C Ancient Berland Circus(計算幾何)

CodeForces - 1C

題目大意:

給出一個正\(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\) 的餘數