1. 程式人生 > 實用技巧 >2020牛客暑期多校訓練營(第二場)B - Boundary - 計算幾何

2020牛客暑期多校訓練營(第二場)B - Boundary - 計算幾何

Description

給定二維平面上的 \(n\) 個點,求一個過原點的圓,使得圓周經過的給定點的數目最大。\(n \le 2000\)

Solution

標答的做法:

同弧所對的圓周角相等

列舉所有點 \(P\),設 \(OP\) 為一條弦,此時若列舉另一點 \(A\),則可確定一圓

當確定 \(OP\) 後,圓的唯一自由量可以視作半徑,而 \(OP\) 在同樣半徑的圓上對應的圓周角相等

因此我們可以對剩下所有點 \(A\) 計算出圓周角 \(\angle OAP\),取眾數即可

考慮到一個角度會有對稱的兩個 \(A\) 與其對應,因此我們強行約束 \(OA \times OP >0\)


其實這樣誤差可能比較大,標程用了分數類之類的操作,比較麻煩

我們考慮直接用三點共圓座標公式解決問題,總體思路和上面的方法相同,不過是把角度換成圓心座標罷了

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

#define int long long
const int N = 2005;

struct point
{
    double x,y;
    double operator * (const point &b)
    {
        return x*b.y-y*b.x;
    }
    double len2()
    {
        return x*x+y*y;
    }
    bool operator < (const point &b) const
    {
        return x==b.x?y<b.y:x<b.x;
    }
} p[N];

int n,ans;

signed main()
{
    ios::sync_with_stdio(false);

    cin>>n;
    for(int i=1;i<=n;i++) cin>>p[i].x>>p[i].y;

    for(int i=1;i<=n;i++)
    {
        map<point,int> mp;
        for(int j=i+1;j<=n;j++)
        {
            double d=p[i]*p[j],d1=p[i].len2(),d2=p[j].len2();
            if(fabs(d)<1e-6) continue;
            double x=(p[i].y*d2-p[j].y*d1)/d, y=(p[i].x*d2-p[j].x*d1)/d;
            ans=max(ans,++mp[{x,y}]);
        }
    }
    cout<<ans+1<<endl;
}