问题描述:
海面上有一些船需要与陆地进行通信,需要在海岸线上布置一些基站。现将问题抽象为,在x轴上方,给出n条船的坐标 p 1 , p 2 , … , p n p_1, p_2, …, p_n p1,p2,…,pn,其中 p i = ( x i , y i ) p_i = (x_i, y_i) pi=(xi,yi), 0 ≤ y i ≤ d 0≤y_i≤d 0≤yi≤d 、 1 ≤ i ≤ n 1≤i≤n 1≤i≤n,在 x x x 轴安放的基站可以覆盖半径为 d d d 的区域内的所有船只,问在 x x x 轴至少要安放几个基站才可以将 x x x 轴上方的船只都覆盖到。
解决思路:
可以使用贪心法来解决。先按照每艘船到x轴距离为d的投影点(右侧那个)对船只进行排序。然后遍历每艘船,如果该船没有被任何一个基覆盖到,那么就在上述的那个点放置一个基站。
实质上就是让没覆盖到的船只处于新基站的最边缘位置,这样这个基站能贪到后续最多的基站。
一点补充(正确性分析):
如下图所示,假设船i之前的船已经分配了基站,那么现在我们要为船i分配基站。
虽然我们不知道船 i i i后面的船在什么位置,但是为了新分配的这个基站能有最大可能覆盖到其他船,显而易见我们会把这个新基站放在绿点处,让船 i i i处于新基站的边缘,也就是说新基站再往右移就已经覆盖不了船 i i i了。因此,我们可以看到在下图的例子中,新基站不仅覆盖了船 i i i,还贪到了船 j j j,而如果新基站放在红点就不能覆盖到船 j j j了。
对所有未被基站覆盖的船执行上述过程,就是一个贪心的过程。
C++源代码:
//
// main.cpp
// CellPlacement
//
// Created by 胡昱 on 2022/1/3.
//
#include <iostream>
#include <cstring>
#include <math.h>
using namespace std;
// 浮点误差
const double minINF = 0.00000000001;
// 最大船只数量
const int maxN = 10000;
// 记录船只的横纵坐标的数组,ships[i][0]为横坐标、ships[i][1]为纵坐标
double ships[maxN + 1][2];
// 记录基站的横坐标的数组,很明显最多也只能有和船只数量一样多的基站
// 可能翻译为 Base Station 好一点,但是这个比较简单
double cells[maxN + 1];
int cell_num;
// 共有n艘船只
int n;
// 基站可以覆盖半径为d的区域
int d;
// 对船只的位置进行排序
// 注意要按照可允许的最远基站点位置排序
int compareByPosition(const void* a, const void* b)
{
double temp = ((double*)a)[0] + sqrt(d * d - ((double*)a)[1] * ((double*)a)[1]) - (((double*)b)[0] + sqrt(d * d - ((double*)b)[1] * ((double*)b)[1]));
// 注意浮点数比较注意预留一定的精度判断
if(-minINF <= temp && temp <= minINF) {
return 0;
}
else if(temp < 0) {
return -1;
}
else {
return 1;
}
}
// 判断该船只是否已被覆盖
bool isCover(double px, double py) {
for(int ci = 0; ci < cell_num; ci++) {
double temp = (px - cells[ci]) * (px - cells[ci]) + py * py - d * d;
if(temp <= minINF) {
return true;
}
}
return false;
}
int main(int argc, const char * argv[]) {
// 共m组测试用例
int m;
cin >> m;
while((m--) > 0) {
// 初始化
memset(ships, 0, sizeof(ships));
memset(cells, 0, sizeof(cells));
cell_num = 0;
// 输入船只数量和基站可覆盖范围半径
cin >> n >> d;
// 输入船只坐标
for(int ni = 0; ni < n; ++ni) {
cin >> ships[ni][0] >> ships[ni][1];
}
// 根据位置对船只进行排序
qsort(ships, n, sizeof(double) * 2, compareByPosition);
// 按顺序遍历每艘船只进行贪心法
for(int ni = 0; ni < n; ++ni) {
// 判断该船只有没有被覆盖,没有的话给它安排一个基站
if(!isCover(ships[ni][0], ships[ni][1])) {
cells[cell_num] = ships[ni][0] + sqrt(d * d - ships[ni][1] * ships[ni][1]);
++cell_num;
}
}
// 输出结果
cout << cell_num << endl;
}
return 0;
}