喷水装置_贪心算法

长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;
}
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玳宸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值