蓝桥杯习题——区间移位

区间移位

问题描述

  • 数轴上有n个闭区间 D 1 , ⋯   , D n D_1,\cdots,D_n D1,,Dn。其中区间 D i D_i Di用一对整数[ a i a_i ai, b i b_i bi]来描述,满足 a i a_i ai < b i b_i bi。已知这些区间的长度之和至少有10000。所以,通过适当的移动这些区间,你总可以使得他们的“并”覆盖[0, 10000]——也就是说[0, 10000]这个区间内的每一个点都落于至少一个区间内。
  • 希望你找一个移动方法,使得位移差最大的那个区间的位移量最小。具体来说,假设你将 D i D_i Di移动到[ a i + c i a_i+c_i ai+ci, b i + c i b_i+c_i bi+ci]这个位置。你希望使得 m a x ∣ c i ∣ max|c_i| maxci最小。

输入格式

  • 输入的第一行包含一个整数n,表示区间的数量。
  • 接下来有n行,每行2个整数 a i a_i ai b i b_i bi,以一个空格分开,表示区间[ a i a_i ai, b i b_i 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 ≤ a i a_i ai < b i b_i bi≤ 10000。

思路

  • 首先是==二分思想==

    • 求最大值的最小或者求最小值的最大,都要使用二分法新鲜知识进入了小白的脑子

    • 在确定方法后,首先要清楚二分什么,这道题我们二分区间最大可以移动的距离,代码中用mid来存储。比如有一个区间为 [ a i , b i ] [a_i, b_i] [ai,bi],则它可以移动的区间范围为 [ a i − m i d , b i + m i d ] [a_i-mid, b_i+mid] [aimid,bi+mid]这里的二分思想还是和礼物分巧克力相同

  • 然后比较容易想到要对输入的n区间进行排序,但怎么排序

    • 我首先想到的是对这n个区间的左端点进行从大到小的排序,但这样可能会造成比较大的区间浪费和较多的移动,因此选择根据右端点进行排序,这样移动的较少就可以覆盖的更多。
    • 如下图所示。若按左端点排序,则区间i应该在区间j的前面(即先放区间i,再放区间j),这时区间j可能就放不了了或需要更大的移动,造成区间浪费。但是如果按右端点排序,区间j在区间i的前面(即先放区间j,再放区间i)。(这里还是解释不清楚,姑且这么理解吧

    在这里插入图片描述

  • 其次是在二分出mid后,Judge函数的书写(即判断当前mid是否满足题意,满足的时候扩大不满足的时候缩小

    • 如何判断当前mid值是否满足要求?
      • 程序中使用max_range变量维护当前可以覆盖的最远坐标
      • 假设现在处理到第j个区间 [ a j , b j ] [a_j,b_j] [aj,bj]。若a[j] - mid <= max_range && b[j] + mid >= max_range,则该区间可以在移动mid单位的限制下继续扩大覆盖范围,继续查找下一个区间(这里还要注意更新max_range的取值,具体细节见下一条);反之,则应当放宽当前的mid限制(即退出Judge函数)。
      • a[j] + mid >= max_range,则max_range = b[j] - a[j] + max_range;否则,max_range = b[j] + mid
    • 还要注意不能改变range数组的值,要备份!!!其中的for循环也要注意!在剩余区间集中找到一个满足条件的即可,不一定非要某一个区间
  • 最后是一些细节

    • 正常二分题中,最后的答案是不可能为小数的,但这道题最后的答案可能为小数(小数只能为0.5,1.5,2.5, ⋯ \cdots )。大多数博客都选择将原来的区间长度10000乘以2来规避小数情况(读入的区间信息也要乘以2)。

C++代码

#include<stdio.h>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

#define MAXN 20000
struct node{
    int a;
    int b;
};
vector<node> range;

// 用于判断mid的函数
bool Judge(int mid);
// 用于C++中的sort函数
bool cmp(node x, node y);

int main(){
    // 输入数据
    int n = 0, i = 0;
    int a = 0, b = 0;
    scanf("%d", &n);
    for(i = 0; i < n; i++){
        scanf("%d %d", &a, &b);
        
        // 规避小数
        a = a * 2;
        b = b * 2;
        
        range.push_back({a, b});
    }
    
    // 根据区间的右端点进行排序
    sort(range.begin(), range.end(), cmp);
    
    // 二分查找
    int left = 0, right = MAXN;
    double result = 0;
    while(left <= right){
        int mid = (left + right) / 2;
        if(Judge(mid)){
            right = mid - 1;
            result = mid;
        } else {
            left = mid + 1;
        }
    }
    
    // 输出结果
    cout << result/2.0 << endl;
    return 0;
}

bool Judge(int mid){
    int i = 0, max_range = 0;
    bool judgement;
    vector<node> temp(range);
    while(true)
	{
		judgement = false;
        // 注意:在剩余区间中找到一个就可以
        // 找到之后还要将其删除
		for(i=0;i<temp.size();i++)
		{
			node temp_node = temp[i];
            if((temp_node.a - mid) <= max_range && (temp_node.b + mid) >= max_range){
            	judgement = true;
            	if(temp_node.a + mid >= max_range)
                	max_range = max_range + temp_node.b - temp_node.a;
            	else
                	max_range = temp_node.b + mid;
                temp.erase(temp.begin()+i);
				break;
        	}
		}
		if(!judgement || max_range >= MAXN) 
            break;
	}
    return max_range >= MAXN;
}

bool cmp(node x, node y){
    return x.b < y.b;
}

参考博客

}
	}
	if(!judgement || max_range >= MAXN) 
        break;
}
return max_range >= MAXN;

}

bool cmp(node x, node y){
return x.b < y.b;
}


## 参考博客

[【蓝桥杯有点意思】区间移位 二分答案-CSDN博客](https://blog.csdn.net/kiko_caoyue/article/details/87922292#:~:text=对于每一个x (也就是mid),可知所有的区间移动距离都小于等于x,那么对于一个区间 [a%2Cb]%2C其移动范围也就是 [a-x%2Cb%2Bx]之间。 然后我们的k从0开始,在区间内找其覆盖,如果k在 [a-x%2Cb%2Bx]之间,那么这个区间就可以把k覆盖,然后我们进行如下讨论: int len,k %3D tb%2Bx%3B 1 2 3 ta是目前的区间的左端点,tb是目前区间的右端点。 如果ta%2Bx>%3Dk,那么我们只需要让区间的左端点移动到k处,右端点此时在k%2Blen处。)
  • 11
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蝴蝶飞过废墟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值