【Comet OJ - Contest #13】E

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_43649416/article/details/102763544

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);
}

猜你喜欢

转载自blog.csdn.net/qq_43649416/article/details/102763544