POJ:1328 Radar Installation 解题思路(采用优先队列和栈实现)、注意点以及部分测试数据
本题选自PKU Online Judge(POJ)——http://poj.org/ 当中的第1328题
题目不难,但是比较经典,主要的思路是采用贪心算法求解,但是需要转化一下才行,网络上也有其他的博主写关于此题的思路,但是我这里主要是采用STL方式进行解题(priority_queue&stack),这使得我的方法写出的代码不会涉及太多循环变量,也没有建立新的数据结构,代码量仅控制在50行以内,先看题。
Radar Installation
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 123563 | Accepted: 27315 |
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.
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
Source
我先用中文大概讲一下题目意思:
给定一个直角坐标系,定义x轴为海岸线,海岸线上方是海,下方是陆地。在海域零星分布一些海岛, 需要要在海岸线上安装若干个雷达覆盖这些海岛,每个雷达的覆盖半径都是相同且固定的。现在给定所有海岛的坐标(x,y), 以及雷达的覆盖半径d,问可以覆盖所有海岛的最小雷达数。
解题思路:
首先可以明确的是:只要存在任意一个海岛位置超出雷达最大覆盖半径(即y>d),则无解.
在所有海岛的 y<=d 的前提下去思考此问题,那么此问题的切入点是需要知道【一个雷达要覆盖一个海岛,其可以安装范围是多少】
以海岛坐标(x,y)为圆心,用雷达覆盖半径d画圆,根据前提条件可知,这个圆与x轴必定存在最少1个(y=d)、最多2个交点(y<d).
可以认为1个交点是2个交点重合的特殊情况,那么这2个交点在x轴上构成的线性区间,可以看作海岛的被覆盖范围在x轴上的投影 (由此就可以把二维的几何问题转化成一维几何问题)
按照所有海岛的x轴坐标,从小到大依次计算每个海岛在x轴上的投影区间(投影可能存在重叠),同时把每个雷达抽象成1个点,那么此问题就转化成:【已知x轴上若干个区间(可能存在交集),现在要往这些区间内放若干个点,问怎样放置这些点,使得每个区间内至少存在一个点,且所放置的点的总量尽可能最少】
可使用贪心算法求解:
根据每个区间的左端点坐标对所有区间从左到右排序:
- 在左起第一个区间A的右端点A.R放置一个点,总放置点数P+1 。
- 若下一个区间B的左端点B.L > A.R, 说明区间A与区间B无交集,此时在区间B的右端点B.R放置一个点,总放置点数P+1,否则说明 区间A与区间B相交, 此时进一步判断,若B.R <= A.R,说明区间B在区间A的内部,此时把原本放置在 A.R 的点移动到 B.R(确保区间B有一个点),总放置点数不变。
- 重复这个过程直到所有区间被遍历完成。
下面给出AC代码:
#include <iostream>
#include <queue>
#include <math.h>
#include <stack>
#include <stdio.h>
using namespace std;
int main(void){
int n, R, index = 0;
while (scanf("%d%d", &n, &R) != EOF){ // 注意,如果你是用cin输入出现"Time Limit Exceeded"的情况,在保证你的代码没有出现因为复杂度上的问题时请用scanf进行输入
index++;
if (n == 0 && R == 0) break;
int num = -2;
priority_queue<pair<float, float> > pqu; // 优先队列,里面按照pair的第一个元素进行由大到小的排列,second递归次之
stack<pair<float, float> > qu; // 将优先队列里面的元素反过来,使由小到大排列
for (int i = 0; i < n; i++){
float x, y;
scanf("%f%f", &x, &y);
if (y > R || y < 0){ // 注意别漏掉y < 0的情况,也就是海岛不能在陆地上
num = -1;
continue;
}
float sta = sqrt(R * R * 1.0 - y * y) + x, end = x - sqrt(R * R * 1.0 - y * y);
pair<float, float> pa(sta, end);
pqu.push(pa);
}
if (num == -1){
cout<<"Case "<<index<<": "<<num<<endl;
continue;
}
num = 0;
while (pqu.empty() != true){ // 倒置优先队列里面的元素到栈当中
pair<float, float> pa = pqu.top();
pqu.pop(), qu.push(pa);
}
while (qu.empty() != true){
pair<float, float> pa = qu.top();
qu.pop(), num++;
while(qu.empty() != true){
pair<float, float> temp = qu.top();
if (temp.second <= pa.first) qu.pop(); // 注意 temp.second <= pa.first当中的<=符号,海岛在雷达所在范围圆上也是成立的
else break;
}
}
cout<<"Case "<<index<<": "<<num<<endl;
}
return 0;
}
在这里需要注意的几点列出如下:
- 在程序当中,如果你是用cin输入,提交后出现"Time Limit Exceeded"的情况,在保证你的代码没有出现因为复杂度上的问题时,请用scanf进行输入 (程序11和19行)
- 输入过程当中注意别漏掉y < 0的情况,也就是海岛位置不能在陆地上(程序20行)
- 海岛是允许刚好在雷达探测的圆上(程序42行)
另外如果你的代码仍然Wrong Answer,先试一试我底下列举出的部分测试数据,一个个测试观察是否正确:
2 5
-3 4
-6 3
4 5
-5 3
-3 5
2 3
3 3
20 8
-20 7
-18 6
-5 8
-21 8
-15 7
-17 5
-1 5
-2 3
-9 6
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 7
9 6
10 5
0 0
2 3
0 2
2 3
2 3
0 2
1 3
3 3
1 2
-3 2
2 4
8 5
2 4
-4 4
-3 3
-3 1
-3 0
-1 0
0 5
6 0
3 0
1 2
-3 1
2 1
3 2
1 2
-3 1
2 1
1 2
0 2
2 3
0 2
2 3
4 -5
4 3
4 3
2 3
6 -9
3 -3
1 2
-3 2
2 1
6 2
1 2
1 2
1 2
-3 1
2 1
0 0
1 2
0 2
2 3
0 2
1 3
3 10
1 10
2 3
4 5
3 5
1 10
2 3
4 5
4 7
1 10
2 3
4 5
0 0
3 9
1 10
2 3
4 5
2 5
0 3
8 3
0 0
运行结果:
Case 1: 1
Case 2: 2
Case 3: 4
Case 4: 1
Case 5: 1
Case 6: -1
Case 7: 3
Case 8: -1
Case 9: 2
Case 10: 1
Case 11: 1
Case 12: -1
Case 13: -1
Case 14: 2
Case 15: 1
Case 16: 1
Case 17: 1
Case 18: -1
Case 19: -1
Case 20: -1
Case 21: 1
本人写出的代码当中还涉及到STL的相关知识,主要是优先队列的运用,不懂可以去查一查文档看看。
如果这题搞懂了可以尝试做一做这一道题POJ 3069——Saruman's Army,思路和上面基本类似。
也欢迎大家访问我的主页,我会分享一些自己学习过程当中的技术和想法,谢谢支持。