蓝桥杯国赛第八届c++ A组 区间移位

题目描述

数轴上有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消耗 < 1000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。

提交时,注意选择所期望的编译器类型。

题目分析

题型:二分查找 + 贪心

思路:

  1. 首先,将各个区间先按右端点升序排序,然后右端点相同的再按左端点升序排序;之所以这样做的原因是(1)右端点从小到大升序,可以保证区间是从左往右覆盖的(2)之后再左端点从小到大升序,两者结合,保证移动的距离最小。
    如果是先按左端点升序排序然后再将右端点升序的话,将不能保证移动的距离最小。(纸上模拟一下就可以知道)

  2. 其次,利用二分查找法得出一个区间移动的值x,然后移动各个区间,查看其能否将0-20000都覆盖。题目所给的是0-10000,但这里因为区间最小可以移动的距离是0.5,为了方便处理,就将所有的区间端点都乘2,最后再除2;
    回到二分法,如果当前x可以满足所有的区间全覆盖,那么就将x变得更小一些,再次检查;如果不可以,那么之前的x就是答案(在二分法函数中需要记录解答)

  3. 然后问题就变成了对每一次的x,如何来移动各个区间。 这里首先需要设置一个变量last,表示已经移动(处理)好的覆盖区域的最右端点。然后判断即将处理的这个区间移动之后是否会超出这个0-last范围,如果不会,那么继续下一个区间;如果会,就要对这个区间进行处理。

    处理的方式有两种,一种是区间移动x之后,该区间左端点将会比last值大,那么就不能实现全覆盖,所以该区间移动的距离不是x,而是该区间长度,记为len;第二种,移动x之后可以实现全覆盖,那么就是移动x(不能移动更多,x就是移动大小的上限)。

    然后更新last值即可。

    以上操作需要用到两层循环,以及标记区间移动结束的变量f;用vetctor容器来存放各个区间,每处理完一个区间,就将该区间从容器中删除。当容器中没有区间时,就代表着区间处理结束,这是全部区间都需要移动的情况;那么当有区间是多余的,也就是不是所有的区间都要进行处理的情况的时候,当出现遍历完内层循环没有区间可以移动(说明剩下的都是无效的区间)就代表区间处理结束。

代码

#include<bits/stdc++.h>
using namespace std;
struct node{
	int l, r;
};
vector<node> v;
int n;
bool cmp(node a, node b){
	if(a.r == b.r)return a.l < b.l;
	else return a.r < b.r;
}
bool check(int x){
	vector<node> w(v);//使用v初始化w
	int last = 0;
	while(true){
		int f = 0; //用来标记是否有区间进行了移动
		for(int i = 0; i < w.size(); i++){
			int len = w[i].r - w[i].l; //区间长度
			//该区间可以移出已经处理好的[0-last]范围之内 
			if(w[i].l - x <= last && w[i].r + x >= last){
				f = 1;
				if(w[i].l + x >= last){//移动x会不能全覆盖 
					last += len;
				} else{
					last = w[i].r + x; //移动最大距离x 
				}
				w.erase(w.begin() + i);//将处理完的区间删除
				break; //找到一个区间就退出for循环,表示该轮last已经更新 
			} 
		} 
		//当 所有区间处理完毕 或者  没有可以移动的区间 的时候,完成值为x时的操作 
		if(w.size() == 0 || f == 0){
			break; 
		}
	} 
	return last >= 20000;
}
int main(){
	while(~scanf("%d", &n)){
		v.clear();
		int ll, rr;
		for(int i = 0; i < n; i++){
			scanf("%d%d", &ll, &rr);
			node no;
			no.l = ll * 2;
			no.r = rr * 2;
			v.push_back(no);
		}
		//对区间进行排序
		sort(v.begin(), v.end(), cmp); 
		double ans = 0;
		int l = 0, r = 20000;
		//二分处理
		while(l <= r){
			int mid = (l + r) / 2;
			if(check(mid)){
				r = mid - 1;
				ans = mid; //记录情况 
			}
			else{
				l = mid + 1;
			}
		} 
		ans /= 2;
		cout << ans << endl;
	}
	return 0;
}

参考博文:https://blog.csdn.net/wrwhahah/article/details/89971530

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

梦想总比行动多

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

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

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

打赏作者

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

抵扣说明:

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

余额充值