NYACM_012

NYACM_012

题目:喷水装置(二)
链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=12
描述:

描述
有一块草坪,横向长w,纵向长为h,在它的橫向中心线上不同位置处装有n(n<=10000)个点状的喷水装置,每个喷水装置i喷水的效果是让以它为中心半径为Ri的圆都被润湿。请在给出的喷水装置中选择尽量少的喷水装置,把整个草坪全部润湿。
输入
第一行输入一个正整数N表示共有n次测试数据。
每一组测试数据的第一行有三个整数n,w,h,n表示共有n个喷水装置,w表示草坪的横向长度,h表示草坪的纵向长度。
随后的n行,都有两个整数xi和ri,xi表示第i个喷水装置的的横坐标(最左边为0),ri表示该喷水装置能覆盖的圆的半径。
输出
每组测试数据输出一个正整数,表示共需要多少个喷水装置,每个输出单独占一行。
如果不存在一种能够把整个草坪湿润的方案,请输出0。
样例输入
2
2 8 6
1 1
4 5
2 10 6
4 5
6 5
样例输出
1
2

分析:

喷嘴位置固定 ,可以根据坐标,半径得出覆盖最大范围,将输入的若干个坐标+范围转换为若干个左右区间坐标,问题转换为从一系列线段中选择最少个数使能够覆盖指定范围。采用贪心算法,从能够覆盖左侧的所有点中选择能够达到的最右侧的数据,然后用剩下的点覆盖剩下的线段部分。

AC Code:
#include <iostream>
#include <cmath>
#include <algorithm>

using namespace std;

//int Data[10000][2] = {0};//用来存储原始数据
typedef struct _Range
{
    double left;
    double right;
}Range;  //为了方便排序,使用结构体存储
Range Data[10000];

double GetWidth(int h,int R)
{
    double ret = 0;
    ret = sqrt(R*R - (h*h / 4.0));
    return ret;
}

bool cmpp(const Range &r1,const Range &r2)
{
    return r1.left < r2.left;
}

int main()
{
    //test();
    //return 0;

    int N;//N 组测试数据
    cin >> N;
    while (N--)
    {
        int n, w, h;
        cin >> n >> w >> h;
        for (int i = 0; i < n; i++)
        {
            //如果有些喷水装置范围过小,覆盖不到宽度,可以忽略
            int tmp_x, tmp_y;
            cin >> tmp_x >> tmp_y;
            if (tmp_y <= h/2)
            {

            }
            else
            {                       
                Data[i].left = tmp_x - GetWidth(h, tmp_y);  //将坐标+范围转换为覆盖的左右边界
                Data[i].right = tmp_x + GetWidth(h, tmp_y);
            }           
        }
        sort(Data, Data + n, cmpp); 

        //现在拿到了所有数据,进行处理,采用贪心算法,总是拿到能够拿到最大范围
        int answer = 0;  //记录选择喷水装置个数
        double left = 0; //当前左侧
        double right = w; //当前右侧
        int index = 0;
        int tmp_index=0;
        while (left<right) //还没覆盖完全
        {
        //  cout << "Current left right is " << left << " " << right << endl;
            int validflag = 0;
            double tmp_right=left;
            for (int i = index; i < n; i++)
            {
                if (Data[i].left > left)
                {
                    break;
                }
                else
                {
                    if (tmp_right < Data[i].right)
                    {                       
                        tmp_right = Data[i].right; //有能够达到更右侧的点,进行更新
                        tmp_index = i;
                        validflag = 1;
                        //cout << "in try phase: current index is " << i << " tmp_right is " << tmp_right << endl;
                    }
                }
            }
            if (validflag == 0)
            {               
                answer = 0;  //不能实现覆盖剩余右侧部分
                break;
            }
            else
            {
                left = tmp_right;  //剩余线段左侧就是刚才选择点能达到的右侧
                answer++;   //使用了一个喷水装置
                index = tmp_index;  //改点已经覆盖了左侧部分 ,只需要从该点右侧进行寻找
                //cout << "final use " << index << " now left  is " << left << endl;
            }
        }
        cout << answer << endl;
    }
    return 0;
}
其他:

对STL还是不够熟悉,自定义排序用的太少。网上搜索有可以直接对二维数组排序的,对指针不熟,只能采用了结构体来代替。前面看了线段树,刚开始还想着用线段树来解决,后来转换一下,还是贪心算法最简单。不过虽然AC,8/392,基本是当前运行结果页AC中最差的了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值