zoukankan      html  css  js  c++  java
  • 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

      

      

    ————全心全意投入,拒绝画地为牢
  • 相关阅读:
    ExecuteNonQuery()返回值
    GridView导入至EXCEL (报错处理:只能在执行 Render() 的过程中调用 RegisterForEventValidation)
    mysql 远程登录
    四舍六入 银行家算法
    linux-grep-tail-find
    spring 事务注解
    aop execution 表达式解析
    事务有效条件
    oracle 日期取 月 日
    spring cloud 定时任务
  • 原文地址:https://www.cnblogs.com/Bw98blogs/p/8438792.html
Copyright © 2011-2022 走看看