【贪心】B030_雷达设备(排序 + 勾股定理)

一、题目描述

假设海岸是一条无限长的直线,陆地位于海岸的一侧,海洋位于另外一侧。

每个小岛都位于海洋一侧的某个点上。

雷达装置均位于海岸线上,且雷达的监测范围为 d,当小岛与某雷达的距离不超过d时,该小岛可以被雷达覆盖。

我们使用笛卡尔坐标系,定义海岸线为x轴,海的一侧在 x 轴上方,陆地一侧在 x 轴下方。

现在给出每个小岛的具体坐标以及雷达的检测范围,请你求出能够使所有小岛都被雷达覆盖所需的最小雷达数目。

输入格式

  • 第一行输入两个整数n和d,分别代表小岛数目和雷达检测范围。
  • 接下来n行,每行输入两个整数,分别代表小岛的x,y轴坐标。
  • 一行数据之间用空格隔开。

输出格式

  • 输出一个整数,代表所需的最小雷达数目,若没有解决方案则所需数目输出“-1”。

数据范围

  • 1 n 1000 1≤n≤1000
输入样例:
3 2
1 2
-3 1
2 1
输出样例:
2

二、题解

方法一:排序 + 勾股定理

思路

这道题是隐式区间贪心问题,但是题目没有说的那么直观,而是给出小岛坐标 (x, y) 与 检测范围的半径 d,让读者计算区间并存储区间信息,下面给出思路。
在这里插入图片描述
由上图易得区间的开始与结束:

  • s e g s [ i ] . b e g i n = x d 2 y 2 segs[i].begin = x - \sqrt {d^2 - y^2}
  • s e g s [ i ] . e n d = x + d 2 y 2 segs[i].end = x + \sqrt {d^2 - y^2}

这样把区间画出来以后,问题可被简化为至少要多少个雷达才能将这些区间覆盖完?

  • 我们将岛屿区间集合 segs[0...N] 按照 end 升序排列。
  • 因为我们是按照 end 升序,所以我们只需考虑下一个点的结尾位置是否能被当前雷达覆盖即可,具体为:
    • 如果下一个岛屿的 end 位置都能覆盖,证明前一个岛屿的 end 位置肯定能被覆盖的了。
    • 否则,证明需要新开一个雷达检测该岛屿,并将当前能被检测到的最远岛屿 Seg cur 指向到该岛屿。

算法

  • 如果某个坐标 y > d,证明无论如何都无法检测到所有岛屿,print(-1)
  • 如果下一个岛屿可以被当前雷达检测到,不用做任何动作,continue 即可。
  • 否则,count++ 表示新开一个雷达检测该岛屿。
import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
    public static void main(String[] args) throws IOException {  
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        
		int N = sc.nextInt();
		int D = sc.nextInt();
		Seg[] segs = new Seg[N];
		
		for (int i = 0; i < N; i++) {
			int x = sc.nextInt();
			int y = sc.nextInt();
			if (D < y) {
				System.out.println(-1);
				return;
			}
			double sqrtX = Math.sqrt(D * D - y * y);
			segs[i] = new Seg(x - sqrtX, x + sqrtX);
		}
		Arrays.sort(segs);
		int count = 1;
		
		Seg cur = segs[0];
		for (int i = 1; i < N; i++) {
			if (cur.end >= segs[i].begin) {
				continue;
			}
			count++;
			cur = segs[i];
		}
		System.out.println(count);
    }
	static class Seg implements Comparable<Seg>{
		double begin, end;
		public Seg(double _begin, double _end) {
		   begin = _begin;
		   end = _end;
		}
		@Override
		public int compareTo(Seg other) {
			return Double.compare(this.end, other.end);
		}
	}
}

复杂度分析

  • 时间复杂度: O ( n l o g n ) O(nlogn)
  • 空间复杂度: O ( n ) O(n)

值得注意的问题

  • Q1:浮点数比较问题。
    A1:用浮点数比较会存在误差,所以建议使用了 2 数之差小于某个精确值 exact value 来判断他们是否相等。
    double exv = 10^6;
    //相应地,if (cur.end >= segs[i].begin) 条件应该改为
    if (cur.end > segs[i].begin + exv {
    	continue;
    }
    附:可以参考【蓝桥杯】赛前准备(二) 这篇文章中「杜绝浮点数比较的章节」
    
  • Q2:实现 Comparable 接口后的如何编写返回值为 int 类型的 compareTo 方法问题。
    A2:
    1. 可以使用静态类 Double.compareTo(double d1, double d2)
    2. 转为对象然后调用自身的 compareTo 方法。
      Double obj1 = new Double("8.5");
      Double obj2 = new Double("11.50");
      int retval =  obj1.compareTo(obj2);
      
发布了691 篇原创文章 · 获赞 151 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_43539599/article/details/105427824
今日推荐