传送门:http://poj.org/problem?id=1328
题目大意:给定一条海岸线即x轴,上方为海,下方为陆地。在海中有n个小岛,希望在海岸线上装雷达,每个雷达辐射区域均为半径为d的圆,问至少要装多少个雷达才能使所有的岛屿都在雷达辐射区域内。
先上代码
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
struct island
{
double leftEdge;//记录在x轴上安装雷达可以辐射到岛时x最左边位置
double rightEdge;//记录在x轴上安装雷达可以辐射到岛的x最右边位置
island(double l = 0, double r = 0)
{
leftEdge = l;
rightEdge = r;
}
};
bool compare(island a, island b)
{
return(a.leftEdge < b.leftEdge);
}
int main()
{
int n, d, t = 1;
cin >> n >> d;
while (d != 0)
{
island islands[1000];
int numofRadar = 0;//记录最小雷达数量
for (int i = 0; i < n; i++)
//输入雷达位置
{
int xPos, yPos;
cin >> xPos;
cin >> yPos;
if (yPos > d)numofRadar = -1;
double tmpLength = sqrt(abs((double)d * d - yPos * yPos));
islands[i].leftEdge = xPos - tmpLength;
islands[i].rightEdge = xPos + tmpLength;
}
sort(islands, islands + n, compare);
if (numofRadar != -1)
{
numofRadar = 1;
}
double leftEdge = islands[0].leftEdge;
double rightEdge = islands[0].rightEdge;
for (int i = 1; i < n; i++)
{
if (islands[i].leftEdge <= rightEdge)//两个范围有相交
{
//cout << "两个范围有相交"<<endl;
leftEdge = max(leftEdge, islands[i].leftEdge);
rightEdge = min(rightEdge, islands[i].rightEdge);
//此时雷达次数不需要增加,但是左右边界需要变化
}
else
//两个范围没有相交,需要重新加雷达
{
if (numofRadar != -1)
{
numofRadar++;//雷达数量++
}
leftEdge = islands[i].leftEdge;
rightEdge = islands[i].rightEdge;
}
}
cout << "Case " << t << ": " << numofRadar << endl;
cin >> n >> d;
t++;
}
return 0;
}
解题思路:
使用贪心算法,与活动选择问题类似。
如果出现有小岛的y>d的情况即没有雷达能覆盖到该小岛的情况,将flag设置为1,最后输出-1。
下面只考虑每个小岛y均小于d的情况:
以每个小岛为圆心做一个半径为d的圆,会与x轴相交与2点,leftEdge和rightEdge可以知道所有安装在这个区间内的雷达均可以覆盖到该岛。以下简称小岛范围和小岛左右边界
创建一个结构体数组记录以每个小岛左右边界,然后按照每个小岛的左边界将海岛进行升序排序。
定义leftEdge和rightEdge参数,用来记录当前最后一个雷达可以安装的范围。(由于小岛按照左边界排序,如果从左到右遍历,某个小岛范围已经不能与之前安装雷达有重叠,则后面的小岛也不会与之前安装雷达有重叠,所以每次只用记录最后一个新安雷达能安装的范围即可)如果新加入的海岛雷达覆盖范围有交集,则不需要再加入新的雷达,如果没有交集则需装新雷达。
算法步骤
1.先选左边界在最左边的岛,此时将雷达数量设为1,这个雷达可安装的位置为当前岛的左右边界之间。将leftEdge和rightEdge赋值。
double leftEdge = islands[0].leftEdge;
double rightEdge = islands[0].rightEdge;
2.新加入一个岛,会出现两种情况
(1)如果该岛的islands[i].rightEdge<=rightEdge则不需加新雷达,只用修改当前最后一个雷达应该安装的范围即可,这个雷达应该安装在新的范围与之前雷达安装范围的交集中。更新当前最后一个雷达可安装位置
leftEdge = max(leftEdge, islands[i].leftEdge);
rightEdge = min(rightEdge, islands[i].rightEdge)
(2)如果该岛的islands[i].rightEdge>rightEdge则需加新雷达,这个雷达可安装范围应该与该小岛的范围相同。更新当前最后一个雷达可安装位置,并将雷达数量+1。
leftEdge = islands[i].leftEdge;
rightEdge = islands[i].rightEdge;
避雷!
1.sort函数要求严格,compare函数中当两者相等的时候必须返回时false否则会报错。
2.sqrt函数有多种重载形式,如果需要在函数中转化数据类型,sqrt((double)d * d - yPos * yPos),虽然VS中编译没有问题但是poj会报compile error。
3.如果poj中出现Presentation Error,说明代码基本是对的,只是在输出中多了或者少了回车或空格之类的问题。
一开始的时候思路有点混乱,将小岛按x坐标升序排序后再从左到右遍历小岛来看是否增加雷达,这种做法不可取的原因是如果有可能存在第t个小岛的y非常高,导致其范围与已存在的雷达可装范围无重叠导致需要新增雷达,但可能后面存在某个y非常小的小岛能被之前安装的雷达覆盖到,所以导致错误。