几何+贪心 | POJ 1328 Radar Installation

658 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天, 点击查看活动详情

题目描述

设海岸线是一条无限长的直线。陆地在海岸线的一侧,大海在另一侧。每个小岛都是位于海上的一个点。而任何安装在海岸线上的雷达,都只能覆盖半径为d的圆形区域,所以如果海中的一个岛屿可以被一个雷达覆盖,它们之间的距离最大为d.

我们使用笛卡尔坐标系,定义海岸线为x轴。海洋在 x 轴上方,陆地在下方。给定每个岛屿在海中的位置,并考虑到雷达装置的覆盖范围的距离,您的任务是编写一个程序来找到覆盖所有岛屿需要安装雷达的最少数量。请注意,岛屿的位置由其 x-y 坐标表示。

雷达装置输入示例
点(1,2) (3,2)(-3,1)

输入

输入由多个测试用例组成。每个情况的第一行包含两个整数 n (1<=n<=1000) 和 d,其中 n 是海洋中的岛屿数,d 是雷达装置的覆盖距离。接下来 n 行,每行包含两个整数,表示每个岛屿位置的坐标。然后,下面用一个空行分隔案例。

输入由包含一对零的线终止\

输出

对于每个测试用例输出一行,包含每组测试用例编号,以及对应需安装的雷达最小数量。
如果不管安装多少雷达,都无法覆盖所有小岛,输出-1

示例输入

3 2
1 2
-3 1
2 1

1 2
0 2

0 0

示例输出

Case 1: 2
Case 2: 1

思路分析

这道题要用到贪心算法,让每一个雷达覆盖较多的点,如何覆盖是一个几何问题. 首先以小岛为圆心d为半径建立一个圆,该圆与x轴有两个交点,两个交点之间的这段直线,即相交区间。在这个区间上任意一点建设雷达都可以覆盖该小岛。如果各个相交区间没有交集,即以各个小岛为圆心建立的圆如果不相交,那么肯定要多个雷达站
如果某一个小岛为圆心的圆与x轴的右交点比另一个小岛为圆心的圆的右交点小,那么这两个小岛一定可以被同一个雷达覆盖。如下图蓝圆的右交点X坐标小于紫圆
image.png
更详细的分析: 首先我们输入小岛坐标并计算出每个小岛的圆与x轴的左右交点,然后再对这些圆排序
由于习惯从左到右数,就用左交点从左到右排序(即X坐标从小到大)
值得一提的是,用了一个临时变量来作比较,double temp = r[0].right 将它的初值设为交X轴最左边的圆的右交点
如果下一个圆的左交点在第一个圆的右边(即大于右交点),并更新比较量为下一个圆的右交点,并且所需雷达数加1
如果某一个圆的右交点比上一个圆的右交点小,那么更新比较量,像上面那个图示意的,更新为蓝圆右交点

AC代码

#include<iostream>
#include<math.h>
#include<algorithm>
using namespace std;
struct radar
{
    double left;//小岛覆盖范围左交点坐标
    double right;//右交点坐标
}r[1001];
int cmp(radar a, radar b)//定义比较函数,用结构体内的变量排序
{
    return a.left < b.left;
}
int main()
{
    int cnt = 0, n, d;
    while (cin >> n >> d && (n || d))//输入n,d,只要不是都为0就进行处理
    {
        cnt++;
        int flag = 0, sum = 1;
        double x, y;
        for (int i = 0; i < n; i++)
        {
            cin >> x >> y;//输入小岛坐标
            if (y > d) flag = 1;
            r[i].left = x - sqrt(d * d - y * y);//计算圆与x轴的左右交点
            r[i].right = x  + sqrt(d * d - y * y);
        }
        sort(r, r + n, cmp);//对小岛构成的圆与x轴的左交点进行从小到大的排序
        if (flag) { cout << "Case " << cnt << ": -1" << endl; continue; }//输出-1
        double temp = r[0].right;
        for (int i = 1; i < n; i++)
            if (r[i].left > temp)
                sum++, temp = r[i].right;  //如果两个圆不相交,那么雷达数目加1,并更新比较量
            else if (r[i].right < temp)
                temp = r[i].right;//如果某一个圆的右交点比上一个圆的右交点小,那么更新比较量
        cout << "Case " << cnt << ": " << sum << endl;//输出最少所需雷达数
    }
    return 0;
}

一点总结

今天终于在6点前写完了,呼~
本来要写另一个文章,但是今天要帮某人写题,这是帮某人写的,就写这个题解好了~