題目鏈接
題意
- 就是給你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);
}