C语言之雷达安装(贪心算法——区间覆盖)

题目描述

假设海岸线是一条无限延伸的直线。它的一侧是陆地,另一侧是海洋。每一座小岛是在海面上的一个点。雷达必须安装在陆地上(包括海岸线),并且每个雷达都有相同的扫描范围 d。你的任务是建立尽量少的雷达站,使所有小岛都在扫描范围之内。

数据使用笛卡尔坐标系,定义海岸线为 x 轴。在 x 轴上方为海洋,下方为陆地。

输入格式

第一行包括 2 个整数 n 和 d,n 是岛屿数目,d 是雷达扫描范围。

接下来 n 行,每行两个整数,为岛屿坐标。

输出格式

一个整数表示最少需要的雷达数目,若不可能覆盖所有岛屿,输出 -1

输入

复制代码
3 2
1 2
-3 1
2 1

输出

复制代码
2

说明/提示

样例 1 解释

数据范围

对于全部数据,n≤1000,d≤2×10^4,∣xi​∣≤2×10^6,0≤yi​≤2×10^4。

代码:

cs 复制代码
#include <stdio.h>
#include <math.h>

#define MAXN 1010

typedef struct {
    double l, r;
} Node;

int n;
double d;
double x[MAXN], y[MAXN];
Node a[MAXN];

int main() {
    scanf("%d %lf", &n, &d);
    
    for (int i = 0; i < n; i++) {
        scanf("%lf %lf", &x[i], &y[i]);
        
        // 判断无解情况
        if (y[i] > d) {
            printf("-1\n");
            return 0;
        }
        
        // 计算雷达可覆盖区间
        double dist = sqrt(d * d - y[i] * y[i]);
        a[i].l = x[i] - dist;
        a[i].r = x[i] + dist;
    }
    
    // 使用冒泡排序按区间右端点排序(不使用qsort指针)
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (a[j].r > a[j + 1].r) {
                // 交换两个结构体
                Node temp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = temp;
                
                // 同时也交换对应的x, y数组元素以保持一致(如果需要的话)
                double temp_x = x[j];
                double temp_y = y[j];
                x[j] = x[j + 1];
                y[j] = y[j + 1];
                x[j + 1] = temp_x;
                y[j + 1] = temp_y;
            }
        }
    }
    
    int ans = 0;
    double last_radar = 0;
    
    for (int i = 0; i < n; i++) {
        if (i == 0) {
            last_radar = a[i].r;
            ans++; // 把第一个雷达放置于第一个区间的右端点
        } else if (last_radar >= a[i].l) {
            continue; // 如果当前岛屿可被覆盖,就不放雷达
        } else {
            last_radar = a[i].r;
            ans++; // 否则放置一个新的雷达
        }
    }
    
    printf("%d\n", ans);
    return 0;
}

上述代码整体思路:

利用的是贪心算法------区间覆盖。

首先明确,因为岛屿都是在海上的,而雷达是只能在陆地上的,所以要求最少雷达数,那就是当雷达在海岸线上时,是最优选择。所以只要岛屿的横纵坐标大于雷达扫描范围d,就不可能覆盖这个岛屿,直接无解。但上述只用纵坐标大于d即可,下面用公式讲述......

先计算如果要覆盖这个岛屿,那么雷达可以安装的范围,也就是求最大值和最小值,只要雷达在这个范围内,(包括边界)肯定就覆盖这个岛屿。有3个岛屿,所以可以求出三个边界。因为要求最小雷达数,所以将右边界进行升序排列。将第一个雷达放在排序后的第一个的右边界上,这样才能保证第一个雷达尽可能覆盖下一个岛屿;然后判断排序后的第二个范围的左边界和第一个的右边界是否有重合位置,如果有重合,则说明第一个雷达在第二个范围内,肯定可以覆盖第二个岛屿,那就不用再添加雷达,但如果没有重合,则说明第一个雷达不能覆盖第二个岛屿,肯定就要再添加一个雷达,这个要添加的肯定也要放在第二个范围的右边界。继续按上述循环不变,完成所有岛屿的遍历......

怎么计算雷达可覆盖范围呢?

double dist = sqrt(d * d - yi * yi);

ai.l = xi - dist;

ai.r = xi + dist;

  • 雷达:必须安装在x轴上,位置为 (radar_x, 0)

  • 岛屿:位置为 (x[i], y[i]),其中 y[i] > 0(在x轴上方)

  • 雷达覆盖半径:d

雷达覆盖岛屿的条件是:岛屿到雷达的距离 ≤ 雷达半径d。每个岛屿对应一个雷达覆盖范围。

复制代码
√[(radar_x - x[i])² + (0 - y[i])²] ≤ d

两边平方:(radar_x - xi)² + yi² ≤ d²→(radar_x - xi)² ≤ d² - yi²

由于 (radar_x - x[i])² ≥ 0,所以要求:d² - yi² ≥ 0 => d ≥ yi。这就是上面为什么只需要纵坐标大于d的原因。

复制代码
|radar_x - x[i]| ≤ √(d² - y[i]²)→x[i] - √(d² - y[i]²) ≤ radar_x ≤ x[i] + √(d² - y[i]²)

上述就是雷达覆盖范围。因为左右两边几乎一样,最好用变量代替,否则特别麻烦。

复制代码
      岛屿(x[i], y[i])
       /|
      / |
  d  /  | y[i]
    /   |
   /    |
雷达位置  dist = √(d² - y[i]²)
(在x轴上)
相关推荐
方也_arkling7 小时前
【Java-Day08】static / final / 枚举
java·开发语言
风吹夏回7 小时前
Python 全局异常处理:从“满屏 try-except”到优雅兜底
开发语言·python
Chengbei117 小时前
一站式源码安全检测工具、云安全 / APP / 小程序源码敏感信息递归多层目录扫描AK、JWT、手机号、身份证等敏感信息
java·开发语言·安全·web安全·网络安全·系统安全·安全架构
llz_1127 小时前
web-第一次课后作业
java·开发语言·idea
kkeeper~7 小时前
0基础C语言积跬步之数据在内存中的存储
c语言·数据结构·算法
小熊Coding8 小时前
Python爬取当当网二手图书项目实战!
开发语言·爬虫·python·beautifulsoup·requests·二手图书
秋98 小时前
Java项目运行5天左右自动宕机:系统性定位与解决方案
java·开发语言·python
xiaoshuaishuai88 小时前
C# 内存管理与资源泄漏
开发语言·c#
lsx2024068 小时前
SVN 检出操作
开发语言
basketball6169 小时前
C++ NULL 和 nullptr 区别 以及 nullptr 的核心实现
java·开发语言·c++