2017省选拔(二)poj1981 Circle and Points (数学)

本文探讨了如何使用单位圆来覆盖平面上的点,并找出最多能覆盖多少个点的问题。通过枚举两点间的距离判断是否能构成圆心,进而统计每个可能圆心覆盖的点数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

直接链接:点击打开链接

You are given N points in the xy-plane. You have a circle of radius one and move it on the xy-plane, so as to enclose as many of the points as possible. Find how many points can be simultaneously enclosed at the maximum. A point is considered enclosed by a circle when it is inside or on the circle. 
 
Fig 1. Circle and Points

Input
The input consists of a series of data sets, followed by a single line only containing a single character '0', which indicates the end of the input. Each data set begins with a line containing an integer N, which indicates the number of points in the data set. It is followed by N lines describing the coordinates of the points. Each of the N lines has two decimal fractions X and Y, describing the x- and y-coordinates of a point, respectively. They are given with five digits after the decimal point. 

You may assume 1 <= N <= 300, 0.0 <= X <= 10.0, and 0.0 <= Y <= 10.0. No two points are closer than 0.0001. No two points in a data set are approximately at a distance of 2.0. More precisely, for any two points in a data set, the distance d between the two never satisfies 1.9999 <= d <= 2.0001. Finally, no three points in a data set are simultaneously very close to a single circle of radius one. More precisely, let P1, P2, and P3 be any three points in a data set, and d1, d2, and d3 the distances from an arbitrarily selected point in the xy-plane to each of them respectively. Then it never simultaneously holds that 0.9999 <= di <= 1.0001 (i = 1, 2, 3). 
Output
For each data set, print a single line containing the maximum number of points in the data set that can be simultaneously enclosed by a circle of radius one. No other characters including leading and trailing spaces should be printed.
Sample Input
3
6.47634 7.69628
5.16828 4.79915
6.69533 6.20378
6
7.15296 4.08328
6.50827 2.69466
5.91219 3.86661
5.29853 4.16097
6.10838 3.46039
6.34060 2.41599
8
7.90650 4.01746
4.10998 4.18354
4.67289 4.01887
6.33885 4.28388
4.98106 3.82728
5.12379 5.16473
7.84664 4.67693
4.02776 3.87990
20
6.65128 5.47490
6.42743 6.26189
6.35864 4.61611
6.59020 4.54228
4.43967 5.70059
4.38226 5.70536
5.50755 6.18163
7.41971 6.13668
6.71936 3.04496
5.61832 4.23857
5.99424 4.29328
5.60961 4.32998
6.82242 5.79683
5.44693 3.82724
6.70906 3.65736
7.89087 5.68000
6.23300 4.59530
5.92401 4.92329
6.24168 3.81389
6.22671 3.62210
0
Sample Output
2
5
5
11
题目大意:

在一个区域面积内(最大为10)有很多个点(用坐标表示),用一个单位圆圈点。问呢能圈的最多的点是多少。

题解:

用单位圆圈点,假如一个点的时候,就让它在圆内就可以了。那么如果是两个点呢。那就得看看这两个点的距离了。如果这两个点的距离小于直径的话,那么我有两种选择,一是让这个两个点必须在圆上,俩一个当然可以不在圆上。当然为了能够圈出更多的点的话,我应该让这两个点在圆上才会使得圆有更多的空间去圈别的点(自己想一想这个)。那么程序就来了。每次枚举两个点,找到这两个点的圆心,然后再从n个点中找与圆心的距离小于半径的点。

主要怎么找圆心,这个网上的数学证明上有很多。看一下代码吧。

ps(其实这篇博客是在做这个题好几天之后写的。当再分析的时候发现了一个问题,就是当枚举的两个点的距离小于直径的时候会得出两个圆心的,那么取哪一个呢,但是代码中我当时取了一个就过了啊。希望得到大牛的指点)

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>

using namespace std;

double x[500],y[300];
double r;
int mx,n;
double getd(double a1,double a2,double b1,double b2)
{
//    printf("%.5lf %.5lf %.5lf %.5lf \n",a1,b1,a2,b2);
//    printf("%.7lf\n",sqrt((a1-a2)*(a1-a2)+(b1-b2)*(b1-b2)));
    return sqrt((a1-a2)*(a1-a2)+(b1-b2)*(b1-b2));
}
void judge(double rx,double ry)
{
    int num=0;
    for(int i=0;i<n;i++)
    {
        //printf("*%d %.7lf %.7lf\n",i,r,getd(x[i],rx,y[i],ry));
        if(getd(x[i],rx,y[i],ry)<r+0.0000001)num++;
    }
    //cout<<num<<endl;
    mx=max(num,mx);
}
void getrh(int i,int j)
{
    double midx=(x[i]+x[j])/2;
    double midy=(y[i]+y[j])/2;
    double ang=atan2(x[i]-x[j],y[j]-y[i]);
    double d=sqrt(1-getd(x[i],midx,y[i],midy)*getd(x[i],midx,y[i],midy));
    double rx=midx+d*cos(ang);
    double ry=midy+d*sin(ang);
    judge(rx,ry);
}
int main()
{
    while(~scanf("%d",&n),n)
    {
        r=1.0;
        mx=1;
        for(int i=0;i<n;i++)
        {
            scanf("%lf%lf",&x[i],&y[i]);
        }
        for(int i=0;i<n;i++)
        {
            for(int j=i+1;j<n;j++)
            {
                if(getd(x[i],x[j],y[i],y[j])>2.0)continue;
                getrh(i,j);
            }
        }
        printf("%d\n",mx);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值