Description
传送门
- 在平面上给定 n 个点和一个 d,要求找到一条直线 l,使得到 l 的距离不超过 d 的点的个数最大。输出该最大点数。
1≤n≤2000,0≤d≤10000
Solution
- 显然这条直线l往两边平移d的距离后的两条直线一定经过分别经过一个点。
- 否则就可以通过调整使其满足。
- 那么我们就可以枚举往左平移的那条线经过的点,现在要求的是经过一个点的所有直线它的右手方向2d的距离最多有多少个点。
- 刚开始我想的是这条直线一定还被另一个点限制,这样再枚举一个点就可以确定一条直线了。但是这样子并不好就算向右2d中右多少个点。
- 我们反过来考虑。
- 对于一个点,它如果要在这条直线的范围内的话,这个直线的与水平线的夹角应该在哪个范围之内。

- 简单的推算后我们知道,对于与枚举的直线经过点O距离2d以外的点,在一个直角三角形的范围内可以被覆盖到。
- 相反的方向也有一个三角形,同理,因为这条直线的夹角在(0,2PI)之间
- 然后对于在2d以内的,它的贡献区间是过原点直线的一边。
- 单个点的贡献区间都是不想交的。
- 离散化之后求最大覆盖就好了。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 2005
#define db double
#define E 1e-9
using namespace std;
const db PI=acos(-1);
int n,d,i,j,k,x[maxn],y[maxn],ans,cnt,tot;
db p,q;
struct line{
db k; int t,fr;
} l[maxn*maxn];
int cmp(line a,line b){
return abs(a.k-b.k)>E&&a.k<b.k || abs(a.k-b.k)<=E&&a.t>b.t;
}
db sqr(db x){return x*x;}
db dis(int i,int j){return sqrt(sqr(x[i]-x[j])+sqr(y[i]-y[j]));}
void insert(db k,int t){tot++,l[tot].k=k,l[tot].t=t,l[tot].fr=i;}
void add(db x,db y){
if (x>=2*PI) x-=2*PI;
if (y>=2*PI) y-=2*PI;
if (x<=y) insert(x,1),insert(y,-1);
if (x>y) insert(x,1),insert(2*PI,-1),insert(0,1),insert(y,-1);
}
int main(){
scanf("%d%d",&n,&d);
for(i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]);
for(int s=1;s<=n;s++) {
tot=0;
for(i=1;i<=n;i++) if (i!=s){
if (x[i]==x[s]) p=((y[i]>y[s])?1:-1)*PI/2; else p=atan(1.0*(y[i]-y[s])/(x[i]-x[s]));
if (x[i]<x[s]) p+=PI;
if (p<0) p+=PI*2;
if (2.0*d>=dis(i,s))
add(p,p+PI);
else {
q=asin(2.0*d/dis(i,s));
add(p,p+q);
add(p+PI-q,p+PI);
}
}
sort(l+1,l+1+tot,cmp);
cnt=0;
for(i=1;i<=tot;i++){
cnt+=l[i].t;
ans=max(ans,cnt);
}
}
printf("%d",ans+1);
}