POJ #1328 Radar Installation 区间贪心 数学题(雾

Description


 

Assume the coasting is an infinite straight line. Land is in one side of coasting, sea in the other. Each small island is a point locating in the sea side. And any radar installation, locating on the coasting, can only cover d distance, so an island in the sea can be covered by a radius installation, if the distance between them is at most d. 


We use Cartesian coordinate system, defining the coasting is the x-axis. The sea side is above x-axis, and the land side below. Given the position of each island in the sea, and given the distance of the coverage of the radar installation, your task is to write a program to find the minimal number of radar installations to cover all the islands. Note that the position of an island is represented by its x-y coordinates. 
 
Figure A Sample Input of Radar Installations


Input

The input consists of several test cases. The first line of each case contains two integers n (1<=n<=1000) and d, where n is the number of islands in the sea and d is the distance of coverage of the radar installation. This is followed by n lines each containing two integers representing the coordinate of the position of each island. Then a blank line follows to separate the cases. 

The input is terminated by a line containing pair of zeros 

Output

For each test case output one line consisting of the test case number followed by the minimal number of radar installations needed. "-1" installation means no solution for that case.

Sample Input

3 2
1 2
-3 1
2 1

1 2
0 2

0 0

Sample Output

Case 1: 2
Case 2: 1

更多测试样例点我:测试样例

 

思路


 

  吐槽时刻,这道题要特别注意精度问题,岛屿位置和雷达半径是整数,如果当成 double 型计算会导致 TLE。这个狗血的TLE一度让我怀疑C++ vector 是不是真的比内置数组慢太多了...事实证明 c++ 的 vector 当成定长数组计算的话还是很快的。

  回到原题,读完问题后,我们发现从无解情况入手是比较容易的。什么时候会无解呢?即存在一个以岛屿位置为圆心、雷达覆盖半径为半径的圆与 y = 0 联立后都没有解,即 delta < 0 ,化简后可以表示为 yi - d > 0 。

  那么什么时候会有解呢?delta >= 0 时会有解,而且方程联立后会得到两个根,雷达必须修在这两个根之间才能保证覆盖到该岛屿。很显然不需要为每个岛屿都修一个雷达站,因为如果两个岛屿在 x 轴上的根的范围是存在交集的话,就可以只修一个雷达站在交集的范围里。

  这时候贪心策略就出来了:我们先对 x 轴上的根区间以左根的大小进行一次排序,然后拿第一个根区间和第二个根区间的交集 tmp 去往后比对,如果之后的根区间和它交集为空,那么代表当前修的雷达站覆盖不了之后的岛屿,需要在这个根区间里再修一个雷达站,并把 tmp 替换成这个新的根区间;如果之后的根区间和它有交集,那么把 tmp 替换为当前的交集,代表当前修的雷达站只有在当前的这个区间里,才能覆盖当前岛屿 ... 如此比对,直到最后一个根区间比对完毕。

  举个例子,现在有四个岛屿以及建雷达时能被覆盖的根区间分别为 (-8, 2) 、(-7, -1)、(0, 0)、(1, 11) 。抽象成二维平面图如下:

    

  取第一个根区间和第二个根区间的交集为 tmp, tmp 为 [-7, -1] 。它和之后的 [0, 0] 比对后,发现交集为空,那么就需要在 [0, 0] 内多修一个雷达站,并替换 tmp 为新的根区间 [0, 0]。之后发现和 [1, 11] 的交集还是为空,那么在 [1, 11] 内多修一个雷达站,并替换 tmp 为 [1, 11] 。最后得到结果为 3。

  在实际敲代码时,可以把 tmp 区间换为一个记录交集右边界的变量。

  

#include<iostream>
#include<algorithm>
#include<vector>
#include<cstdio>
#include<cmath>
using namespace std;

struct Island {
    int x;
    int y;
    double delta;
    double left_root;
    double right_root;
};
const int MAX_N = 1000;
vector<Island> vec1; //vec1 存储岛屿信息

bool cmp (const Island& a, const Island& b) {
    return a.left_root < b.left_root;
}

int main(void) {
    int case_num = 0;
    int n; //岛屿数
    int d; //雷达覆盖半径
    while( cin >> n >> d) {
        if (n == 0 && d == 0) break;
        case_num++;
        vec1.resize(n+1);
        bool is_all_cover = true;
        for (int i = 1; i <= n; i++) {
            cin >> vec1[i].x >> vec1[i].y;
            vec1[i].delta = d * d - vec1[i].y * vec1[i].y; //将系数4忽略后的delta,便于计算开方
            //与y = 0 联立,有解时
            if (vec1[i].delta >= 0) {
                double sqrt_val = sqrt(vec1[i].delta);
                vec1[i].left_root = vec1[i].x - sqrt_val;
                vec1[i].right_root = vec1[i].x + sqrt_val;
            }
            //存在一个岛屿无法被覆盖,问题无法解决
            else {
                is_all_cover = false;
            }
        }
        //无解情况, 1.雷达覆盖半径是负数 2.存在岛屿无法被覆盖
        if (d < 0) {
            cout << "Case " << case_num << ": " << -1 << endl;
            continue;
        }
        if (!is_all_cover){
            cout << "Case " << case_num << ": " << -1 << endl;
            continue;
        }
        //有解情况,解决雷达站重复覆盖问题
        std::sort(vec1.begin() + 1, vec1.end(), cmp);
        int radar_num = 1; //雷达站数目
        double tmp = vec1[1].right_root;
        for (int i = 2; i <= n; i++) {
            if (vec1[i].left_root > tmp) {
                radar_num++;
                tmp = vec1[i].right_root;
            }
            else if (vec1[i].right_root < tmp) {
                tmp = vec1[i].right_root;
            }
        }
        cout << "Case " << case_num << ": " << radar_num << endl;
        vec1.clear();
    }
    vec1.clear();
    vector<Island>().swap(vec1);
    return 0;
}
View Code

 

  

  

posted @ 2018-02-10 15:03  bw98  阅读(191)  评论(0编辑  收藏  举报