题意
有一个长度为 l l l,宽度为 h h h 的矩形,给你 n n n 个半径为 r r r 的圆,问你最少选多少个圆才能使整个矩形被全部覆盖?如果无解,输出 − 1 -1 −1。本题有多组数据。
思路
首先,我们优先考虑怎样将宽度完全覆盖。
如果当前半径小于宽度,也就说明它不能覆盖整个宽度,对答案,没有影响,不用计入。
然后,我们再来考虑怎样覆盖长度。
如上图,有效覆盖的区域其实就是被蓝色矩形框起来的部分,并且它一定有效覆盖宽度(废话,之前都已经考虑完宽度了),所以我们考虑将这个长方形压缩成一条线段,这样也就变成了一个区间完全覆盖问题。
那我们该如何确定这条线段的左右端点呢?
和上图一样,我们可以用勾股定理来求出最大有效覆盖半径,左端点就是圆心位置减去它,右端点就是圆心位置加上它,即:
l = x − r 2 − ( h 2 ) 2 , r = x + r 2 − ( h 2 ) 2 l=x-\sqrt{r^{2}-(\frac{h}{2})^{2}},r=x+\sqrt{r^{2}-(\frac{h}{2})^{2}} l=x−r2−(2h)2,r=x+r2−(2h)2
随后便是求区间完全覆盖问题,我们将其按左端点从小到大排序,然后记录一个 r r r 表示已经覆盖过的最大右端点。每次将目前没覆盖过的线段左端点 s s s 更新,随后枚举当前区间,如果目前区间左端点小于等于 s s s,也就说明此区间可以用来覆盖,然后将 r r r 更新。如果 r r r 没有被更新且还没有覆盖完,也就说明无解,直接退出。
#include<bits/stdc++.h>
using namespace std;
double n,l,h;
struct node{
double l,r;
}a[1000001];
int cnt;
bool cmp(node a,node b){return a.l<b.l;}
double max(double a,double b){return a<b?b:a;}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(a,0,sizeof(a));cnt=0;
scanf("%lf %lf %lf",&n,&l,&h);
for(int i=1;i<=n;i++)
{
double x,r;
scanf("%lf %lf",&x,&r);
if(r<=h/2) continue;
a[++cnt].l=x-sqrt(r*r-(h/2)*(h/2));
a[cnt].r=x+sqrt(r*r-(h/2)*(h/2));
}
sort(a+1,a+cnt+1,cmp);
bool flag=1;
int i=1,ans=0;
double r=0.0;
while(r<l)
{
ans++;
double s=r;
while(a[i].l<=s&&i<=cnt)
r=max(r,a[i].r),i++;
if(s==r&&s<l){flag=0;break;}
}
if(flag) printf("%d\n",ans);
else puts("-1");
}
return 0;
}