此题应转化为区间覆盖问题,数轴上有 n 个闭区间 [ai, bi],选尽量少的区间覆盖一条指定线段 [s, t]。
将区间按 b 从小到大排序,用 sum 记录当前在线段上覆盖到的位置,每次对整个闭区间扫描,
扫描结果:
1. 线段上有的区间覆盖不到,则无法完成覆盖,跳出扫描循环;
2. 选择能覆盖到 sum 位置且最长的闭区间,用它来覆盖,贪心,保证选的区间少,sum += q[i].b - sum。
代码如下:
#include<iostream>
#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
const int MAXN = 10000 + 5;
int x[MAXN], r[MAXN];
struct qu
{
double a, b;
} q[MAXN];
bool cmp(qu a, qu b) //按 b 升序排列
{
return a.b < b.b;
}
int main()
{
int t, n, w, h;
cin >>t;
while(t--)
{
cin >>n >>w >>h;
int s = 0;
int tx, tr;
while(n--)
{
cin >>tx >>tr;
if(2 * tr > h) //只保存装置直径比草坪宽度大的
{
x[s ++] = tx;
r[s - 1] = tr;
}
}
if(s == 0)
{
cout <<0 <<endl;
continue;
}
h /= 2; //宽度减半,求区间的 a,b
for(int i = 0; i < s; i ++)
{
q[i].a = x[i] - sqrt(r[i] * r[i] - h * h);
q[i].b = x[i] + sqrt(r[i] * r[i] - h * h);
}
sort(q, q + s, cmp); //排序
double sum = 0;
int cnt = 0;
while(sum < w)
{
double len = 0; //当前能覆盖的长度
for(int i = 0; i < s; i ++) //对每个闭区间判断
if(q[i].a <= sum && q[i].b - len > sum) //选择满足条件的且最优的
{
len = q[i].b - sum;
}
if(len == 0) //有的区间覆盖不到
{
cnt = 0;
break;
}
sum += len; //覆盖位置后移
cnt ++; //装置数增加
}
cout <<cnt <<endl;
}
return 0;
}