题目描述
长 L L L 米,宽 W W W 米的草坪里装有 n n n 个浇灌喷头。每个喷头都装在草坪中心线上(离两边各 W / 2 W/2 W/2 米)。我们知道每个喷头的位置(离草坪中心线左端的距离),以及它能覆盖到的浇灌范围。
请问:如果要同时浇灌整块草坪,最少需要打开多少个喷头?
输入格式
输入包含若干组测试数据。
第一行一个整数 T T T 表示数据组数;
每组数据的第一行是整数 n n n、 L L L 和 W W W;
接下来的 n n n 行,每行包含两个整数,给出一个喷头的位置和浇灌半径(上面的示意图是样例输入第一组数据所描述的情况)。
输出格式
对每组测试数据输出一个数字,表示要浇灌整块草坪所需喷头数目的最小值。如果所有喷头都打开也不能浇灌整块草坪,则输出 − 1 −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 % 100\% 100% 的数据, n ≤ 15000 n \leq 15000 n≤15000。
解题思路
- 输入数据,并计算喷水器的有效覆盖区间;
- 按照覆盖区间的开始结点从小到大排序;
- 从左到右依次处理每个区间;
- 每次选择开始结点小于或等于上一次选择区间的结束结点,且结束结点最远的区间;
此处特别说明区间如何计算,具体如下图所示:
标准程序
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
struct node {
double begin, end;
} a[15005];
bool cmp(node x, node y) { return x.begin < y.begin; }
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n, L, W;
scanf("%d%d%d", &n, &L, &W);
int m = 0;
for (int i = 1; i <= n; i++) {
int x, r;
scanf("%d%d", &x, &r);
if (r <= W / 2.0) continue; //如果满足条件 说明喷水器无效
m++;
//根据勾股定理计算喷水器覆盖的范围
a[m].begin = x - sqrt(r * r - (W / 2.0) * (W / 2.0));
a[m].end = x + sqrt(r * r - (W / 2.0) * (W / 2.0));
}
sort(a + 1, a + m + 1, cmp); //按照区间开始点排序
bool f = false;
int ans = 0;
double st = 0;
for (int i = 1; i <= m; i++) {
int j = i;
//找出后续区间中开始点小于st,结束点最远的区间,作为备选区间
double mxend = -1;
while (j <= m && a[j].begin <= st) {
mxend = max(mxend, a[j].end);
j++;
}
//如果所有结束点都小于st,说明无解
if (mxend < st) break;
ans++; //区间数量+1
if (mxend >= L) { //如果区间结束点大于等于目标L,则停止循环
f = true;
break;
}
st = mxend; //更新 st
i = j - 1; //指向指向下一个位置
}
if (f)
printf("%d\n", ans);
else
puts("-1");
}
return 0;
}