在平面直角坐标系中,给出n个点,都在x轴上方,记第i个点为((x_i,y_i)),现在请求出在x轴寻找尽可能少的点数数目作为圆心,以r为半径的圆,能够覆盖所有的点,(nleq 1000)。
解
此题直接做不好做,重在转换模型,现在转换为我们最熟悉的区间问题,于是发现对于一个点A((x,y)),能够覆盖它的点B((l,0))作为圆心,到达极限时,也就是(AB=r)之时,于是可以列出方程
也就是
就是
是
...
容易发现,当(r^2<y^2)时,就不存在圆B可以覆盖点A了,这个要特判,发现了马上输出无解。
于是对于任意一个点i,而言当且仅当圆心B((l,0)),(lin[l-sqrt[2]{r^2-y^2},l+sqrt[2]{r^2-y^2}])时,圆B能够覆盖i。
处理出这个,于是问题转化为有n个区间,用最少的点被所有区间包含的点的数目,这是一个区间问题,记第i个区间为([l_i,r_i]),我们首要考虑排序。
不妨按左端点排序,现在对于其中一个区间i考虑,它被那些点包含,设(j)为用的最后一个点所在位置,如果(j< l_i),显然i不可能包含原来已经有的点,因此只能新开一个点,令(j=r_i),如果(l_ileq j),那么令(j=min(r_i,j))。
正确性:显然新开一个是正确的,对于用原来的(j=min(r_i,j))来说,因为我们是按左端点排序,那么对于前面的区间包含j的区间讲,它的左端点必然小于i的左端点,而对于它们的右端点必然有大于j,现在j变小了,自然不可能超出它们的右端点,而(r_igeq l_i),所以在小不会小过(l_i),自然也不会小过前面的区间,于是我们证明的这样操作不会使原来的包含关系改变。
但是并不能证明不开比新开优秀,这就是微扰法捉襟见肘的地方了,于是考虑决策包容,对于开一个新的点,它所在的选择范围必然是在第i个区间所包含的点内,但是不开一个新的区间,以后开一个新的区间,又和它同等优秀,但是选择范围可以达到任意实数,所以后者包含前者,自然后者会更加优秀。
得证。
于是总上所素,我们只需要把点转化为区间,再把区间按照左端点排序,然后记录最后一个已经开的点所在坐标i,如果对于第i区间([l_i,r_i])满足(l_ileq j),就令(j=min(j,r_i)),否则就新开一个点,令(j=r_i),
时间复杂度显然(O(n))。
参考代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define il inline
#define ri register
#define ll long long
#define Size 1500
using namespace std;
struct inter{
double l,r;
il bool operator<(const inter&x)const{
return l<x.l;
}
}I[Size];
il void read(int&);
template<class free>
il free Min(free,free);
int main(){
int n,d,ans(0);
read(n),read(d);
for(int i(1),x,y;i<=n;++i){
read(x),read(y);
if(d*d-y*y<0)return puts("-1"),0;
I[i].l=x-sqrt((ll)d*d-(ll)y*y);
I[i].r=x+sqrt((ll)d*d-(ll)y*y);
}sort(I+1,I+n+1);double gzy(-1e16);
for(int i(1);i<=n;++i)
if(I[i].l<=gzy)gzy=Min(gzy,I[i].r);
else ++ans,gzy=I[i].r;printf("%d",ans);
return 0;
}
template<class free>
il free Min(free a,free b){
return a<b?a:b;
}
il void read(int &x){
x^=x;ri char c;while(c=getchar(),c==' '||c=='
'||c=='
');
ri bool check(false);if(c=='-')check|=true,c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
if(check)x=-x;
}