题目考点:二分法
题目:
问题描述
数轴上有n个闭区间D1,…,Dn。其中区间Di用一对整数[ai, bi]来描述,满足ai < bi。已知这些区间的长度之和至少有10000。所以,通过适当的移动这些区间,你总可以使得他们的“并”覆盖[0, 10000]——也就是说[0, 10000]这个区间内的每一个点都落于至少一个区间内。
你希望找一个移动方法,使得位移差最大的那个区间的位移量最小。
具体来说,假设你将Di移动到[ai+ci, bi+ci]这个位置。你希望使得maxi |ci|最小。
输入格式
输入的第一行包含一个整数n,表示区间的数量。
接下来有n行,每行2个整数ai,bi,以一个空格分开,表示区间[ai, bi]。保证区间的长度之和至少是10000。
输出格式
输出一个数,表示答案。如果答案是整数,只输出整数部分。如果答案不是整数,输出时四舍五入保留一位小数。
样例输入
2
10 5010
4980 9980
样例输出
20
样例说明
第一个区间往左移动10;第二个区间往右移动20。
样例输入
4
0 4000
3000 5000
5001 8000
7000 10000
样例输出
0.5
样例说明
第2个区间往右移0.5;第3个区间往左移0.5即可。
数据规模和约定
对于30%的评测用例,1 ≤ n ≤ 10;
对于100%的评测用例,1 ≤ n ≤ 10000,0 ≤ ai < bi≤ 10000。
分析:
首先分析,有很多范围在[0,10000]区间要在数轴上移动直到完全覆盖[0,10000],画一下图,即为
画图后,不难看出要覆盖这个数轴,移动距离可以为很多值,比如把最左的移到最右,最右的移到最左,或者从左到右依次填充。,题目让我们求的是区间能覆盖[0,10000]这个范围的移动距离最小值min。
这个时候就可以想到二分法,小于min的移动距离都不能使区间覆盖[0,k],而大于min的移动距离,都可以使区间覆盖[0,k]。符合二分法的条件,移动距离单调上升且在某一临界一侧的值全部不符合条件而另一侧全部符合条件。
思路
1.运用二分法,left=0,right=10000,在[left,mid] 和 [mid+1,right]间不断判断mid,更改left=mid+1或right=mid,最终求出移动距离的[left,right]范围,left即为本题答案。
2.怎么判断mid是否可以,要将区间按右边界排序,从右边界最小的开始从0填充数轴,如果能覆盖10000,即该移动距离合法。
为什么按右边界排序
右边界决定距离上一个区间左边界是否更近,如下图,按左边界排序,1号[200,9000],2号[400,600],移动过程如下,2号显然移动了很长距离。
按右边界排序,1号[400,600] 2号[200,9000]移动距离较短
3.小数部分,样例2为一个小数0.5,从题目要求上可以分析出,小数部分,只能为0.5。区间距离差1的两个区间,要填充1,将1拆分成两个部分,其中最小值只能为0.5。所以为了避免小数,将区间全部乘2,得到left后,再将其除2。注意精度。
代码:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
public class Main {
static ArrayList<section> array = new ArrayList<>();
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n =sc.nextInt();
for(int i=0;i<n;i++) {
array.add(new section(sc.nextInt()*2, sc.nextInt()*2));
}
sc.close();
Collections.sort(array);
int left = 0,right = 20000;
while(left < right) {
int mid = (left + right)/2;
if(check(mid)) {
right = mid;
}else {
left = mid + 1;
}
}
if(left%2==0) {
System.out.print(left/2);
}else {
System.out.print(left*1.0/2);
}
}
static boolean check(int mid) {
ArrayList<section> temp = new ArrayList<>(array);
int k=0; //从0开始填充数轴,k为区间覆盖区域的右边界
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; //即将填充的区间,没有覆盖已填充区域,则右边界k+整个区间
}else {
k = end + mid; //即将填充的区间,覆盖部分已填充区域,则右边界k=end+mid
}
flag = true;
temp.remove(i); //删除已填充的区间
break;
}
}
if(!flag||k>=20000) break;
}
return k>=20000;
}
static class section implements Comparable<section>{
private int start;
private int end;
public section(int a,int b) {
this.start = a;
this.end = b;
}
@Override
public int compareTo(section o2) {
// TODO Auto-generated method stub
if(this.end!=o2.end)
return this.end<o2.end?-1:1;
else
return this.start<o2.start?-1:1;
}
}
}