喷水系统

https://loj.ac/problem/10002

题目描述

  有n个喷头,每个喷头都在花坛的一定位置,每个喷头有一定的喷水的半径,使一个长L,宽W米的花坛全部被水淋到,求至少需要开几个喷头。无法满足则输出-1。

思路

  我们把花坛这个二维的矩形看成一维的线段,那么喷头的有效喷水距离可以看为每个喷头喷水范围与花坛边缘的两交点的长度,即s[i].l= pos-sqrt(r*r-(w/2)*(w/2)),s[i].r=pos+sqrt(r*r-(w/2)*(w/2)),不过注意r<w/2的喷头是“无用的”。那么问题就转化为已知n个区间和一条线段,求选取最少的区间全部覆盖这条线段。那么我们只需将区间按左端点降序排序,假设现已满足[1,t]区间,顺序寻找区间,找到满足是s[i].l<=t的右端点最大的区间,更新t=s[i].r即可。判断无法覆盖时也就比较显然了。

代码

#include <bits/stdc++.h>
using namespace std;
struct aa
{
    double l,r;
}a[15500];
bool cmp(aa x,aa y)
{
    return x.l<y.l;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,l,w;
        scanf("%d%d%d",&n,&l,&w);
        int cnt=0;
        for(int i=0;i<n;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            if(y<=w/2)continue ;
            a[++cnt].l=(double)x-sqrt(y*y-w*w/4.0);
            a[cnt].r=(double)x+sqrt(y*y-w*w/4.0);
        }
        sort(a+1,a+cnt+1,cmp);
//        for(int i=1;i<=cnt;i++)
//            printf("%lf %lf\n",a[i].l,a[i].r);
        double t=0;int ans=0,i=1;
        bool f=1;
        while(t<l)
        {
            ans++;
            double s=t;
            for(;a[i].l<=s&&i<=cnt;i++)
                if(t<a[i].r)t=a[i].r;
            if(t==s&&s<l)
            {
                f=0;
                printf("-1\n");
                break ;
            }
        }
        if(f)printf("%d\n",ans);
    }
    return 0;
}
02-13 16:34