2017年第八届蓝桥杯Java程序设计本科B组决赛个人题解汇总:
https://blog.youkuaiyun.com/daixinliangwyx/article/details/90184941
第六题
标题:区间移位
交题测试地址:http://lx.lanqiao.cn/problem.page?gpid=T451
数轴上有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。
资源约定:
峰值内存消耗 < 256M
CPU消耗 < 2000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。
解法:题目要求最小的位移量,答案最小为0最大为10000,因此这个问号很明显就是二分,直接二分答案来得到满足条件的最小位移量。我们可以假设当前区间是从0开始的,右端点nowright也是0,然后通过check函数来得到当前位移量能否完全覆盖[0,10000],主要就是遍历所有给出的区间不断在一定条件下移动来更新当前区间的右端点nowright,如果nowright最终会大于等于10000说明能够完全覆盖,所以可以先对区间按右端点从大到小排序从而更早的获得更大nowright方便提前退出所有区间的遍历。特别注意,如果最小位移量出现小数那也只能是0.5,因为刚开始区间都是整数,如果一个区间位移0.4,那么另一个区间就需要位置0.6,0.6大于0.5,因此两个区间平分分别位移0.5、0.5是最优的,为了方便处理,避免位移出现小数,可以把区间值都乘2([0,10000]也变为[0,20000]),最后的最小位移量答案除以2即可。
代码:
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
public class Main {
public static InputReader in = new InputReader(new BufferedInputStream(System.in));
public static PrintWriter out = new PrintWriter(System.out);
public static int n, l, r, leftcanto;
public static qj[] q = new qj[10010];
public static void main(String[] args) {
n = in.nextInt();
for (int i = 0; i < n; i++) {
q[i] = new qj();
q[i].l = in.nextInt() * 2;
q[i].r = in.nextInt() * 2;
}
Arrays.sort(q, 0, n);
l = 0;
r = 20000;
while(l <= r) {
int mid = (l + r) / 2;
if(check(mid) == 1) r = mid - 1;
else
l = mid + 1;
}
if (l % 2 == 0) {
out.println(l/2);
out.flush();
} else {
out.println(l/2 + ".5");
out.flush();
}
out.close();
}
static int check(int x) {//x:允许的最大位移量
int nowright = 0;
for (int i = 0; i < n; i++) {
if (q[i].l >= nowright && q[i].l-x <= nowright) nowright = Math.max(nowright, q[i].r-(q[i].l-nowright));//可以进行左移的区间,需要判断左端点能否与nowright接上
else if (q[i].l < nowright) {//可以进行右移的区间,仅用来更新nowright
leftcanto = Math.min(q[i].l+x, nowright);//得到符合条件的最优右移位置
nowright = Math.max(nowright, q[i].r+leftcanto-q[i].l);
}
if (nowright >= 20000) return 1;//已经覆盖了[0,20000],可以提前退出遍历
}
return 0;
}
static class qj implements Comparable<qj>{
int l, r;
@Override
public int compareTo(qj o) {
if (this.r - o.r != 0) {
return 1;
} else {
return 0;
}
}
}
static class InputReader {
public BufferedReader reader;
public StringTokenizer tokenizer;
public InputReader(InputStream stream) {
reader = new BufferedReader(new InputStreamReader(stream), 32768);
tokenizer = null;
}
public String next() {
while (tokenizer == null || !tokenizer.hasMoreTokens()) {
try {
tokenizer = new StringTokenizer(reader.readLine());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return tokenizer.nextToken();
}
public String nextLine() {
String str = null;
try {
str = reader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return str;
}
public int nextInt() {
return Integer.parseInt(next());
}
public long nextLong() {
return Long.parseLong(next());
}
public Double nextDouble() {
return Double.parseDouble(next());
}
public BigInteger nextBigInteger() {
return new BigInteger(next());
}
public BigDecimal nextBigDecimal() {
return new BigDecimal(next());
}
}
}