问题描述

  数轴上有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。

解法一:

解析:

刚开始想着按区间的左端点排序,二分答案之后依次枚举,使得区间尽量向右就行了,但是写出来只有80分。

想了想发现,选取最左面的区间并不是最优的,比如[1000,2000]和[1200,1300]两个区间当我们的now(当前覆盖的最右端点 )是900,二分的答案是300,我们选取第一个区间先向左靠,这是now=1900,此时第二个区间就无法派上用场,但是如果先选取第二个区间再选取第一个区间,now就会变成2300.

所以我们得出的结论是在做区间可以到达now的情况下,选取右端点最小的区间。

做法:

先将每个区间按照右端点从小到大排序,

每个区间活动范围是[l-x,r+x]

二分答案之后再二分出满足r+x>=now的右端点最小的区间,然后从这个区间开始依次枚举找到第一个满足l-x<=now的区间,使得区间覆盖now的同时向右靠即可

另外值得一提的是小数只能是0.5,因为刚开始区间都是整数,如果一个区间唯一0.4,那么另一个就是0.6,大于0.5.所以为了方便处理,把区间值乘2,最后再除2即可

  1. import java.io.*;
  2. import java.util.*;
  3. class S implements Comparable<S>
  4. {
  5. int l;
  6. int r;
  7. public S(int l,int r) {
  8. this.l=l;
  9. this.r=r;
  10. }
  11. @Override
  12. public int compareTo(S o) {
  13. // TODO Auto-generated method stub
  14. if(this.r!=o.r)
  15. return (int) (this.r-o.r);
  16. else {
  17. return (int) (this.l-o.l);
  18. }
  19. }
  20. }
  21. public class Main {
  22. static int N=20000;
  23. static S[]s=new S[10100];
  24. static int []vis=new int [10100];
  25. static int n;
  26. public static void main(String[] args) throws IOException {
  27. BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
  28. int now =0;
  29. String str=br.readLine();
  30. for(int i=0;i<str.length();i++) {
  31. now=now*10+(int)(str.charAt(i)-'0');
  32. }
  33. n=now;
  34. int sum=0;
  35. for(int i=0;i<n;i++) {
  36. now=0;
  37. str=br.readLine();
  38. int a = 0,b;
  39. for(int j=0;j<str.length();j++) {
  40. if(str.charAt(j)==' ') {
  41. a=now;
  42. now=0;
  43. }
  44. else {
  45. now=now*10+(int)(str.charAt(j)-'0');
  46. }
  47. }
  48. b=now;
  49. sum+=b-a;
  50. s[i]=new S(a*2,b*2);
  51. }
  52. Arrays.sort(s,0,n);
  53. int l=0,r=N;
  54. while(l<=r) {
  55. int mid=(l+r)/2;
  56. //System.out.println(mid);
  57. if(check(mid)==true) {
  58. r=mid-1;
  59. }
  60. else {
  61. l=mid+1;
  62. }
  63. }
  64. if(l%2==0) {
  65. System.out.println(l/2);
  66. }
  67. else
  68. System.out.println(((double)(l)*1.0)/2.0);
  69. }
  70. static boolean check(int x) {
  71. int now=0;
  72. for(int i=0;i<10100;i++) {
  73. vis[i]=0;
  74. }
  75. while(true)
  76. {
  77. int flag=0;
  78. int l=0,r=n;
  79. while(l<=r) {
  80. int mid=(l+r)/2;
  81. if(s[mid].r+x>=now) {
  82. r=mid-1;
  83. }
  84. else l=mid+1;
  85. }
  86. for(int i=l;i<n;i++) {
  87. if(s[i].l<=x+now&&vis[i]==0) {
  88. flag=1;
  89. vis[i]=1;
  90. if(now<=s[i].l+x) now+=s[i].r-s[i].l;
  91. else now =s[i].r+x;
  92. break;
  93. }
  94. }
  95. if(now>=20000) return true;
  96. if(flag==0) return false;
  97. }
  98. }
  99. }

解法二:

题解:

首先,maxi |ci|最小,求解这种最大值最小的问题应该想到用二分答案试一试.答案呈现一种单调性,比如第一个样例中答案是20,20是以下的数都不满足要求(就是无论怎么移动都无法覆盖所有的点),所以我们把答案的区间分成0—10000进行二分,0–19的结果都无法满足.19–10000都可以满足,而20就是那个临界点,也是要求解的答案.

二分答案模板:

  1. int left = 0, r = 20000;
  2. while (left < r) {
  3. int mid = (left + r) >> 1;
  4. if (ok(mid)) {
  5. r = mid;
  6. } else {
  7. left = mid + 1;
  8. }
  9. }

上面说了答案是0–10000,那为什么这里是0–20000呢.因为题目中可能出现0.5这种结果,而我们二分的区间每次是1增加,1,2,3,4…10000,那怎么才能将0.5这种结果也算上呢.那我们就将区间扩大2倍,从0–20000,这样虽然每次还是递增1,求解出答案后再/2就是最后结果.比如第二个样例,求解出的结果是1,然后我们/2 == 0.5

最后,重点就放在了这个ok()函数上面,先来看下代码:

  1. static boolean ok(int mid) {
  2. ArrayList<E> tem = (ArrayList<E>) list.clone();
  3. int k = 0;//表示到底能不能包括整个区间 0 -- 20000
  4. while (true) {
  5. boolean flag = false;
  6. for (int i = 0; i < tem.size(); i++) {
  7. int s = tem.get(i).s;
  8. int end = tem.get(i).end;
  9. if (s - mid <= k && end + mid >= k) {
  10. int len = end - s;
  11. if (s + mid >= k) {
  12. k += len;
  13. } else {
  14. k = end + mid;
  15. }
  16. tem.remove(i);
  17. flag = true;
  18. break;
  19. }
  20. }
  21. if (!flag || k >= 20000) break;
  22. }
  23. return k >= 20000;
  24. }

mid代表的是二分答案的结果,看这个结果到底满足要求吗.所以每一个mid代表每个区间可以移动的距离,区间可以向左移动,也可以向右移动,那么区间就可以移动成 [left-mid,r-mid] 或 [left + mid,r + mid].第一行tem集合用来存放所有的区间E(E是自己写的类,2个属性left和r,分别代表左端点和右端点),k 表示区间现在覆盖到哪里了,从0开始.

  1. if (s - mid <= k && end + mid >= k)

s - mid 表示区间向左移动,移动后看左端点是否能在k的后面,如果不能:那么说明这个区间无论向左都无法覆盖k,所以这个区间不满足. end + mid表示区间向右移动,和上面同理:如果不大于K表示这个区间也不满足条件,因为无论向右如何移动都无法到达k

  1. if (s + mid >= k)

既然满足上面的条件,那么区间就可以向左移动或向右移动来进行覆盖.

s + mid >= k,那么k可以移动得最大距离就 len = end -mid,这个区间得左端点直接移动到k,所以最终k = k +len

s + mid < k,那么这个区间左端点向右移动都在k后面,所以k最终变成k = end + mid.

上面的情况都是向右移动,下面的情况是向左移动,道理都一样

s + mid >= k,如果是左端点向左移动到k,k最终还是= k + len(向左移动不存在s + mid < k 这种情况)

  1. tem.remove(i);
  2. flag = true;
  3. break;

每次找到一个区间都把区间从tem中删除,然后终端for循环,从头又继续寻找区间.

完整代码

  1. import java.util.ArrayList;
  2. import java.util.Collections;
  3. import java.util.Scanner;
  4. public class Main {
  5. static int n;
  6. static ArrayList<E> list = new ArrayList<E>();
  7. public static void main(String[] args) {
  8. Scanner sc = new Scanner(System.in);
  9. n = sc.nextInt();
  10. for (int i = 0; i < n; i++) {
  11. list.add(new E(sc.nextInt() * 2, sc.nextInt() * 2));
  12. }
  13. Collections.sort(list);
  14. int left = 0, r = 20000;
  15. while (left < r) {
  16. int mid = (left + r) >> 1;
  17. if (ok(mid)) {
  18. r = mid;
  19. } else {
  20. left = mid + 1;
  21. }
  22. }
  23. if (left % 2 == 0) {
  24. System.out.println(left / 2);
  25. } else {
  26. System.out.println(left * 1.0 / 2);
  27. }
  28. }
  29. static boolean ok(int mid) {
  30. ArrayList<E> tem = (ArrayList<E>) list.clone();
  31. int k = 0;//表示到底能不能包括整个区间 0 -- 20000
  32. while (true) {
  33. boolean flag = false;
  34. for (int i = 0; i < tem.size(); i++) {
  35. int s = tem.get(i).s;
  36. int end = tem.get(i).end;
  37. if (s - mid <= k && end + mid >= k) {
  38. int len = end - s;
  39. if (s + mid >= k) {
  40. k += len;
  41. } else {
  42. k = end + mid;
  43. }
  44. tem.remove(i);
  45. flag = true;
  46. break;
  47. }
  48. }
  49. if (!flag || k >= 20000) break;
  50. }
  51. return k >= 20000;
  52. }
  53. }
  54. class E implements Comparable<E> {
  55. int s, end;
  56. public E(int s, int end) {
  57. this.s = s;
  58. this.end = end;
  59. }
  60. public int compareTo(E o) { //按照右端点进行排序,小的在前面,大的在后面.
  61. if (end != o.end)
  62. return end > o.end ? 1 : -1;
  63. else
  64. return s < o.s ? -1 : 1;
  65. }
  66. }

Java实现蓝桥杯历届试题区间移位的更多相关文章

  1. Java实现蓝桥杯 历届试题 k倍区间

    历届试题 k倍区间 时间限制:2.0s 内存限制:256.0MB 问题描述 给定一个长度为N的数列,A1, A2, - AN,如果其中一段连续的子序列Ai, Ai+1, - Aj(i <= j) ...

  2. Java实现蓝桥杯历届试题分考场

    历届试题 分考场 时间限制:1.0s 内存限制:256.0MB 提交此题 问题描述 n个人参加某项特殊考试. 为了公平,要求任何两个认识的人不能分在同一个考场. 求是少需要分几个考场才能满足条件. 输 ...

  3. Java实现蓝桥杯历届试题兰顿蚂蚁

    历届试题 兰顿蚂蚁 时间限制:1.0s 内存限制:256.0MB 提交此题 问题描述 兰顿蚂蚁,是于1986年,由克里斯·兰顿提出来的,属于细胞自动机的一种. 平面上的正方形格子被填上黑色或白色.在其 ...

  4. Java实现蓝桥杯历届试题回文数字

    历届试题 回文数字 时间限制:1.0s 内存限制:256.0MB 提交此题 问题描述 观察数字:12321,123321 都有一个共同的特征,无论从左到右读还是从右向左读,都是相同的.这样的数字叫做: ...

  5. Java实现蓝桥杯历届试题高僧斗法

    历届试题 高僧斗法 时间限制:1.0s 内存限制:256.0MB 提交此题 锦囊1 锦囊2 问题描述 古时丧葬活动中经常请高僧做法事.仪式结束后,有时会有"高僧斗法"的趣味节目,以 ...

  6. Java实现蓝桥杯历届试题买不到的数目

    历届试题 买不到的数目 时间限制:1.0s 内存限制:256.0MB 提交此题 锦囊1 锦囊2 问题描述 小明开了一家糖果店.他别出心裁:把水果糖包成4颗一包和7颗一包的两种.糖果不能拆包卖. 小朋友 ...

  7. Java实现 蓝桥杯 历届试题 斐波那契

    试题 历届试题 斐波那契 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 斐波那契数列大家都非常熟悉.它的定义是: f(x) = 1 - (x=1,2) f(x) = f(x-1) ...

  8. Java实现 蓝桥杯 历届试题 小计算器

    历届试题 小计算器 时间限制:1.0s 内存限制:256.0MB 问题描述 模拟程序型计算器,依次输入指令,可能包含的指令有 1. 数字:'NUM X',X为一个只包含大写字母和数字的字符串,表示一个 ...

  9. Java实现 蓝桥杯 历届试题 小数第n位

    历届试题 小数第n位 时间限制:1.0s 内存限制:256.0MB 问题描述 我们知道,整数做除法时,有时得到有限小数,有时得到无限循环小数. 如果我们把有限小数的末尾加上无限多个0,它们就有了统一的 ...

随机推荐

  1. ASP.NET Core Blazor 初探之 Blazor Server

    上周初步对Blazor WebAssembly进行了初步的探索(ASP.NET Core Blazor 初探之 Blazor WebAssembly).这次来看看Blazor Server该怎么玩. ...

  2. 08JAVA基础关键字(final、static)以及抽象类和接口

    一.关键字 1.final 修饰类 修饰变量 修饰成员方法 该类为最终类,不能被继承 该变量为常量 该成员方法不能被重写 2.static (1).生命周期 随着类的加载而加载 (2).特点 被本类所 ...

  3. video 全屏,播放,隐藏控件。

    requestFullscreen全屏具体实现 1.进入全屏 function full(ele) { if (ele.requestFullscreen) { ele.requestFullscre ...

  4. Spring-mvc 配置文件applicationContext.xml

    相关jar包(4.3.16.RELEASE) <!-- Spring mvc 基础jar包,maven 依赖 --> <dependency> <groupId>o ...

  5. git推送代码问题之:ERROR: [abcdefg] missing Change-Id in commit message footer

    一.问题: 在日常的工作中,使用git推送代码时会出现以下报错,“missing Change-Id in commit message” : qinjiaxi:$ git push origin H ...

  6. 实战!我用 Wireshark 让你“看得见“ TCP

    每日一句英语学习,每天进步一点点: 前言 为了让大家更容易「看得见」 TCP,我搭建不少测试环境,并且数据包抓很多次,花费了不少时间,才抓到比较容易分析的数据包. 接下来丢包.乱序.超时重传.快速重传 ...

  7. 判断对象oStringObject是否为String

    1.操作符 (1)typeof操作符 格式:result=typeof variable 返回值: undefined 值未定义 boolean 布尔值 string 字符串 number 数值 ob ...

  8. Django之ORM多表增删改操作

    关系表的操作语句: 以上一节中创建的书籍.出版社.作者.作者信息表为例进行: 增: # 一对一 # (1)类属性外键关联,使用外键约束属性直接进行对象关联插入 author_detail_obj=mo ...

  9. redis python操作

    1.基于连接池方式实现对五个数据类型操作,每种数据类型2个操作 2.基于spring-data-redis 基于jedis来实现对五种数据类型操作,每种数据类型实现两个操作,包括事务 以上为基于jav ...

  10. Oracle备份与恢复详解

    http://www.360doc.com/content/10/1015/15/3267996_61218717.shtml --------摘自 360doc 为了能有效地备份和恢复数据库,建议大 ...