更多JAVA版答案移步我的博客:蓝桥杯JAVA版答案汇总
本题考查
二分查找
思路
每一次二分查找的mid都代表每次判断的移动距离,若该移动距离合法则进一步缩小该移动距离(除以2),直到找到合法与不合法的边界,即最后循环结束的left就是合法移动距离的最小值。
-
为什么使用二分查找?
设本题的最短移动距离为min,则小于min的移动距离都不能是区间总长度大于等于给定长度k,大于等于min的移动距离都可以使区间总长度大于等于给定长度k,所以符合二分查找的条件,某一临界一侧全部不符合条件而另一侧全部符合条件。 -
为什么要将区间按照右端点大小进行排序,并按照顺序取区间?
1.若区间是乱序,若给定长度k为20,两个区间[0,10]、[8,20]若区间乱序,先取出了[8,20]区间则需要先将该区间移到[0,12],然后再把[0,10]区间移到[12,22],但实际上若排好序的话,并不需要移动即可覆盖给定区间,所以乱序情况的选择不一定是最优选择。
2.若区间是按照左端点大小进行排序,若给定长度k为12,给定两区间[0,10]、[2,4];若按照左端点排序则最后结果为[0,10]、[10,12]移动距离为8;若按照右端点排序则最后结果为[0,2]、[2,12]移动距离为4。由此可以看出按照左端点大小排序的结果也不一定是最优选择
3.若区间是按照右端点大小进行排序,能够保证每一次填充的区间一定是距离未填充区间左端点最近的区间,因为每次都是取所有能够取到的区间中右端点最小的那个 -
如何判断本次mid代表的移动距离是否合法
遍历所有区间,若区间按照右端点从小到大的顺序填充进未填充区间后,填充区间的右端点大于等于给定长度,则说明该移动距离下,这些区间能够覆盖题目给定的长度 -
为什么要将区间长度乘2
题目给定的样例中存在移动0.5长度的例子,所以便于计算需要将区间长度乘2,计算完临界值之后再除以2
AC代码
import java.util.Collections;
import java.util.LinkedList;
import java.util.Scanner;
public class Main {
static LinkedList<Section> list = new LinkedList<Section>();
static class Section implements Comparable<Section> {
int start, end;
public Section(int start, int end) {
this.start = start;
this.end = end;
}
public int compareTo(Section another) {
if (end != another.end) return end > another.end ? 1 : -1;
else return start < another.start ? -1 : 1;
}
}
static boolean judge(int mid) {
LinkedList<Section> temp = new LinkedList<Section>(list);
int k = 0;
while (true) {
boolean flag = false;
for (int i = 0; i < temp.size(); i++) {
int start = temp.get(i).start;
int end = temp.get(i).end;
if (start - mid <= k && end + mid >= k) {
if (start + mid >= k) k += end - start;
else k = end + mid;
temp.remove(i);
flag = true;
break;
}
}
if (!flag || k >= 20000) break;
}
return k >= 20000;
}
public static void main(String[] args) {
Scanner scaner = new Scanner(System.in);
int n = scaner.nextInt();
for (int i = 0; i < n; i++) list.add(new Section(scaner.nextInt()*2, scaner.nextInt()*2));
scaner.close();
Collections.sort(list);
int left = 0, right = 20000;
while (left < right) {
int mid = (left + right)/2;
if (judge(mid)) right = mid;
else left = mid + 1;
}
if (left % 2 == 0) System.out.println(left/2);
else System.out.println(left*1.0/2);
}
}