长L米,宽W米的草坪里装有n个浇灌喷头。每个喷头都装在草坪中心线上(离两边各W/2米)。我们知道每个喷头的位置(离草坪中心线左端的距离),以及它能覆盖到的浇灌范围。
请问:如果要同时浇灌整块草坪,最少需要打开多少个喷头?
输入格式:
输入包含若干组测试数据。
第一行一个整数T表示数据组数。
每组数据的第一行是整数n、L和W的值,其中n≤10 000。
接下来的n行,每行包含两个整数,给出一个喷头的位置和浇灌半径。
如图1所示的示意图是样例输入的第一组数据所描述的情况。
输出格式:
对每组测试数据输出一个数字,表示要浇灌整块草坪所需喷头数目的最小值。如果所有喷头都打开还不能浇灌整块草坪,则输出-1。
输入样例:
3
8 20 2
5 3
4 1
1 2
7 2
10 2
13 3
16 2
19 4
3 10 1
3 5
9 3
6 1
3 10 1
5 3
1 1
9 1
输出样例:
6
2
-1
数据范围与提示:
对于100%的数据,n≤15000。
思路:
由于喷水器的喷水半径不一样,且每个喷水器的坐标也不同,所直接采用坐标贪心或者采用半径大小贪心都不太合适,这里采用每个喷水器在草坪边缘的两个交点来排序。
设每个喷水器i都会和草坪的一条边相交于两点lefti和righti,那么我们对所有节点,对lefti由小到大排序,如果lefti大小相同,则按照righti由大到小排。
在排序前其实应该判断lefti的最小值是否大于0,若最小的lefti都大于0,那么肯定不可能把草坪全部润湿,同时判断最大的righti和草坪的长的关系。同时把left和right均小于0或者均大于草坪长的喷水器去掉,因为根本用不上。这样得到的若干喷水器就是合法喷水器的组合。
根据left值从小向大找,其中left小于0且right值最大的那个节点肯定是第一个需要的喷水器,这样当该喷水器去掉之后,该喷水器的边缘到草坪的右边缘就构成了一个新的问题,再重复上面的步骤即可。
#include<iostream>
#include<algorithm>
#include<cmath>
struct Pen{ //定义存储左右坐标的结构体数组
double left;
double right;
}pen[15000];
int cmp(Pen x,Pen y){
return x.left<y.left; //按照左坐标从小到大排列
}
using namespace std;
int main(){
int N;
cin >> N;
while(N--){
int n;
cin >> n;
double w,h;
cin >> w >> h;
for(int i=0;i<n;i++){
double xi,ri;
cin >> xi >> ri;
double flag;
if(ri>h/2)
flag = sqrt(ri*ri-h*h/4);
else
flag = 0.0;
pen[i].left = xi-flag;
pen[i].right = xi+flag;
}
sort(pen,pen+n,cmp);
int count = 0;
double star =0.0,last; //last先记录右坐标然后再传递给last
for(int i=0;i<n;i++){
if(pen[i].left <= star){ //排序后刚开始的左坐标肯定小于等于0,否则就不可能全覆盖,找到第一个左坐标小于等于star的
last = pen[i].right; //暂时存储右坐标
/*寻找最大的右坐标*/
while(pen[i].left <= star){ //下一个装置的左坐标一定要小于上一个装置的右坐标,否则无法完全覆盖
last = max(last,pen[i].right); //较大的坐标作为右坐标给last
i++;
if(i == n)
break;
}
star = last; //star记录满足条件的下一个装置的右坐标
i--; //退回,准备继续寻找下一个满足条件的装置
count++;
}
if(star >= w) //star现在为坐标右点,若大于则说明已经全部覆盖
break;
}
if(star >= w)
cout << count <<endl;
else
cout << "-1" <<endl;
}
return 0;
}