POJ 1328 Radar Installation(贪心+区间覆盖)

原题地址:

http://poj.org/problem?id=1328

题意:将一条海岸线看成X轴,X轴上面是大海,海上有若干岛屿,给出岛屿的n个位置和雷达的覆盖半径d,要求在海岸线上建雷达,在雷达能够覆盖全部岛屿情况下,求雷达的最少使用量。

解题思路

这道题的提问方式来看,是很明显的贪心算法的应用,但和背包问题、性价比问题的贪心算法方式有所不同。

一开始想的是对所有岛屿的纵坐标降序排序,在排序后的点对应的横坐标上建雷达,尽可能覆盖这周边岛屿。后来觉得完全没有道理,只能推翻重来。
后来受到网上大神们的启发,利用贪心算法转化到平面几何+最少区间问题。具体的思路如下:

  1. 每个岛屿能被这种半径为d的雷达范围是有限的,这个范围是由以岛屿为圆心,半径为d的圆与x轴的交点决定的(交点个数为0代表不可能被感应到,个数为1或2都是可能被感应到的)。这两个交点的值left/right可以通过平面几何计算得到,那么在[left, right]范围内放置雷达都能感应到这个岛屿,因此这个范围内必须放置一个雷达。
  2. 贪心思想的体现就是让一个雷达的位置尽可能向右覆盖更多的区间。对所有区间的左界left升序排序,并依次对前后区间进行操作。用r来记录前一雷达可以摆放的最右界。
    举例来说,对于已经记录的前一区间的右界r,如果当前区间的左界大于r,说明这两个区间没有交集,必须在第二个区间增加雷达,而这个新的r即第二个区间的右界;如果当前区间的左界大于r,说明这两个区间有交集,就不必增加新的雷达,新的r更新为min(r,第二个区间的右界)。

注意点

  • 由于涉及到几何的平方、开方计算,因此中间变量都要声明为double
  • 异常的情况如下:圆和x轴无交集、雷达范围无效、岛屿纵坐标为负
  • 前几次提交时由于没有想清楚,在两区间有交集时,将r直接更新为新区间的右界,后来想想是很诡异的(这样雷达就无法保证前面的区间了),所以必须更新为两者的最小值,这样才能保证两者都被覆盖。
  • G++超时,C++过了而且很快,浪费了我一个小时编译器你妹- -

AC代码

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#define MAXN 1005
using namespace std;

typedef struct node
{
    double left, right;
    bool operator < (const node &A) const //指定排序规则,以区间的左界升序
    {
        return left < A.left;
    }
}Interval;

int main()
{
    int n, kase = 0;
    double d;
    Interval cover[MAXN];
    while (cin >> n >> d)
    {
        if (n == 0 && d == 0) break;
        kase++;
        double s, t;
        bool possible = true;
        for (int i = 0; i<n; ++i) //以每个岛屿为圆心画半径为d的圆,将它与x轴的交集记录到该岛屿所在区间
        {
            cin >> s >> t;
            if (t > d || d <= 0 || t < 0) //不可能的情况:圆和x轴无交集、雷达无效、岛屿无效
            {
                possible = false;
                continue;
            }
            //计算出岛屿可能被雷达侦测到的左右边界
            double range = sqrt(d*d-t*t);
            cover[i].left  = s-range;
            cover[i].right = s+range;
        }
        if (possible == false)
        {
            printf("Case %d: -1\n", kase);
            continue; //处理下一个case
        }
        sort(cover, cover+n); //区间左界升序

        int radar = 1;
        double r = cover[0].right; //r记录前一雷达能摆放的最右界
        for (int i = 1; i<n; ++i)
        {
            if (cover[i].left > r) //情况1:新区间超出前一雷达最右界
            {
                radar++; //在新区间放置雷达
                r = cover[i].right;  //更新最右界为新区间右界
            }
            else //情况2:新区间和前一雷达有交集(或包含)
                r = min(r, cover[i].right);
        }
        printf("Case %d: %d\n", kase, radar);
    }
    return 0;
}

内存占用:256K 耗时:32ms (G++ TLE)
算法复杂度: O(nlogn)

posted @ 2017-04-05 20:29  Lecholin  阅读(166)  评论(0编辑  收藏  举报