题目链接
题意
- 就是给你n(n≤2000)个整数点,让你求一条直线,使得距离这条直线小于等于d的点数最多
题解
- 首先直接求显然是很难的,不妨倒着思考
- 也就是假如你知道了答案的直线,然后想象两条距离他为d的两条平行线,我们先约定将两条直线组合起来,也就是一条动另一条跟着动,那么我们可以将其中一条的位置移动使得最边缘的点到达这条直线上,显然这样不会对答案造成影响,那么现在我们可以枚举这个节点,即靠在这条直线上的点,那么然后我们怎么找出这条直线的斜率呢?(斜率确定了那么答案也就确定了),可以考虑对于其他的n−1个点,计算出每一个点被上述两条直线构成的组合夹在其中时的斜率范围,然后就转化为了最打区间覆盖问题了,这里我才用的方法是直接将double离散化,然后转化为区间加,区间最大值问题,这个unique是找的C++ reference上的代码改了一下,开始自己写的数组版本被卡了
复杂度
- O(n2logn)
代码
#include<bits/stdc++.h>
using namespace std;
#define eps 1e-8
#define pi acos(-1.0)
const int maxn=2005;
int n,d,tot,f[10*maxn];
double a[10*maxn];
struct point{int x,y;}p[maxn];
struct segment{double l,r;}s[4*maxn];
int sgn(double k) {return k<-eps?-1:(k<eps?0:1);}
int dis(int i,int j) {return (p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y);}
template <class T>
T unique_ (T first, T last) {
if (first==last) return last;
T result = first;
while (++first != last) if (!( sgn(*result - *first)==0)) *(++result)=*first;
return ++result;
}
int main() {
scanf("%d %d",&n,&d);
for(int i=1;i<=n;i++) scanf("%d %d",&p[i].x,&p[i].y);
int ans=0;
for(int i=1;i<=n;i++) {
tot=0;
for(int j=1;j<=n;j++) if(j!=i) {
if(dis(i,j)<=4*d*d) {
double theta=atan2(p[j].y-p[i].y,p[j].x-p[i].x);
s[++tot]=segment{theta,theta+pi};
}else {
double theta=atan2(2*d,sqrt(dis(i,j)-4*d*d)),init=atan2(p[j].y-p[i].y,p[j].x-p[i].x);
s[++tot]=segment{init,init+theta};
s[++tot]=segment{init+pi-theta,init+pi};
}
}
for(int j=1;j<=tot;j++) s[tot+j]=segment{s[j].l+2*pi,s[j].r+2*pi};
int cnt=0;
for(int j=1;j<=2*tot;j++) a[++cnt]=s[j].l,a[++cnt]=s[j].r;
sort(a+1,a+cnt+1);
int k=unique_(a+1,a+cnt+1)-a-1;
for(int j=1;j<=k;j++) f[j]=0;
for(int j=1;j<=2*tot;j++) {
int L=lower_bound(a+1,a+k+1,s[j].l-eps)-a;
int R=lower_bound(a+1,a+k+1,s[j].r-eps)-a;
f[L]+=1,f[R+1]-=1;
}
for(int j=1;j<=k;j++) f[j]+=f[j-1],ans=max(ans,f[j]);
}
printf("%d\n",ans+1);
}