Boundary 2020牛客多校第二场 (计算几何)

这篇博客讨论了一道计算几何题目,要求找到以原点为边界的圆中包含最多点的数量。博主提供了O(n^2)的解决方案,通过计算中垂线找出最多的交点,并提醒注意精度问题。AC代码运行时间为345ms。

原题题面

Given nnn points in 2D plane. Considering all circles that the origin point (0,0)(0,0)(0,0) is on their boundries, find the one with the maximum given points on its boundry. Print the maximum number of points.

输入描述

The first line contains one integer (1≤n≤2000)(1≤n≤2000)(1n2000), denoting the number of given points.
Following {n}n lines each contains two integers x, y$ (∣x∣,∣y∣≤10000)$, denoting a given point (x,y)(x,y)(x,y).
It’s guaranteed that the points are pairwise different and no given point is the origin point.

输出描述

Only one line containing one integer, denoting the answer.

输入样例

4
1 1
0 2
2 0
2 2

输出样例

3

题面解析

首先很容易就能想到O(n3)O(n^3)O(n3)的暴力方法,但显然会超时。
先放出题人的方法,出题人给出了圆周角的方法计算(下图)
在这里插入图片描述
笔者自己实际去尝试了以下,发现有部分细节并未说清楚(多个点平行时的处理),故不提供该种解法的代码。
笔者采用中垂线的办法去计算。
先算枚举两个点做两条中垂线,算完交点后取出最多的那个就是答案。
所以只要两重循环开O(n2)O(n^2)O(n2)的复杂度去枚举点,然后用pair和map存下每个点出现的次数并比较即可。
需要注意的是,这题似乎卡了精度,貌似1e-10是可以的,读者可以自行尝试。

AC代码(345ms)

#include<bits/stdc++.h>
using namespace std;
double eps=1e-10;
typedef pair<double, double> Pair;
int epsCmp(double x, double y)//误差范围内相等
{
    if (fabs(x-y)<eps)
        return 0;
    if (x>y)
        return 1;
    return -1;
}
struct point
{
    double x;
    double y;
};
struct line//ax+by+c=0
{
    double a;
    double b;
    double c;
};
point points[2010];
map<Pair,long long> hashMap;
void solve1()
{
    int t;
    scanf("%d", &t);
    for(int i=1; i<=t; i++)
    {
        scanf("%lf %lf", &points[i].x, &points[i].y);
    }
    long long ans=0;//初值不能是1,因为可能会出现n=1的情况
    for(int i=1; i<=t-1; i++)
    {
        hashMap.clear();
        for(int j=i+1; j<=t; j++)
        {
            point O=point{0.0, 0.0};//原点
            point P=point{points[i].x, points[i].y};//第一个枚举点P
            point A=point{points[j].x, points[j].y};//第二个枚举点A
            line OP=line{P.x, P.y, -(P.x*P.x+P.y*P.y)/2.0};//OP中垂线
            line OA=line{A.x, A.y, -(A.x*A.x+A.y*A.y)/2.0};//OA中垂线
            if (epsCmp(AP.a*OP.b, AP.b*OP.a)!=0)//排除平行情况
            {
                double x0=(OP.b*AP.c-OP.c*AP.b)/(OP.a*AP.b-OP.b*AP.a);
                double y0=(OP.a*AP.c-OP.c*AP.a)/(OP.a*AP.b-OP.b*AP.a);
                ans=max(ans, ++hashMap[Pair(x0,y0)]);//比较结果
            }
        }
    }
    printf("%lld\n", ans+1);//有n个圆形相同就有n+1个点被覆盖
}
int main()
{
//    ios_base::sync_with_stdio(false);
//    cin.tie(0);
//    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    long long test_index_for_debug=1;
    char acm_local_for_debug;
    while(cin>>acm_local_for_debug)
    {
        cin.putback(acm_local_for_debug);
        if (test_index_for_debug>100)
        {
            throw runtime_error("Check the stdin!!!");
        }
        auto start_clock_for_debug=clock();
        solve1();
        auto end_clock_for_debug=clock();
        cout<<"\nTest "<<test_index_for_debug<<" successful"<<endl;
        cerr<<"Test "<<test_index_for_debug++<<" Run Time: "
            <<double(end_clock_for_debug-start_clock_for_debug)/CLOCKS_PER_SEC<<"s"<<endl;
        cout<<"--------------------------------------------------"<<endl;
    }
#else
    solve1();
#endif
    return 0;
}

后记

赛后被学弟带飞,不愧是后浪拍死前浪…
DrGilbert 2020.7.13

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值