ACcoders Problem 2034 题解

题意

有一个长度为 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=xr2(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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值