POJ 1981 Circle and Points 计算几何

题目大意

给出平面上的一些点,求一个单位圆最多能够覆盖多少点。

思路

数据范围300,但是没有用,多组数据就是要卡 O(n3) ,然而常数优化的比较好的话在POJ能过,但是BZ上还是过不了。我们需要寻找一种 O(n2logn) 的做法。
做法就是枚举每个点,做一个一这个点为圆心的单位圆。之后将所有在这个圆里的点弄出来,以这些点为圆心做单位圆,与开始的单位圆会产生一段圆弧,最后求哪一段圆弧被覆盖的次数最多就是答案。

CODE

O(n3) 水过版

#define _CRT_SECURE_NO_WARNINGS

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 510
#define EPS 1e-7
using namespace std;

struct Point{
    double x, y;

    Point(const double &_, const double &__):x(_), y(__) {}
    Point() {}

    Point operator +(const Point &a)const {
        return Point(x + a.x, y + a.y);
    }
    Point operator -(const Point &a)const {
        return Point(x - a.x, y - a.y);
    }
    Point operator *(const double &a)const {
        return Point(x * a, y * a);
    }
    Point operator /(const double &a)const {
        return Point(x / a, y / a);
    }

    void Read() {
        scanf("%lf%lf", &x, &y);
    }
}point[MAX];

inline double Calc(const Point &p1, const Point &p2)
{
    return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}

inline Point Change(const Point &v)
{
    return Point(-v.y, v.x);
}

int points;
bool v[MAX];

int stack[MAX], top;

inline int GetAns(const Point &p1, const Point &p2)
{
    double dis = Calc(p1, p2) / 2;
    if(dis > 1.0)   return 0;
    Point o = Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2) + (Change(p1 - p2) / (dis * 2)) * sqrt(1 - dis * dis);
    int re = 0;
    for(int i = 1; i <= top; ++i)
        re += Calc(point[stack[i]], o) <= 1.0 + EPS;
    return re + 1;
}

int main()
{
    while(scanf("%d", &points), points) {
        for(int i = 1; i <= points; ++i)
            point[i].Read();
        int ans = 1;
        for(int i = 1; i <= points; ++i) {
            top = 0;
            for(int j = 1; j <= points; ++j)
                if(i != j && Calc(point[i], point[j]) < 2.0)
                    stack[++top] = j;
            for(int j = 1; j <= top; ++j)
                ans = max(ans, GetAns(point[i], point[stack[j]]));
        }
        printf("%d\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值