区间移位蓝桥杯

时间限制: 1Sec 内存限制: 128MB 提交: 24 解决: 6

题目描述

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

思路:二分枚举答案,对于这种要求所有求解量中最大的那个要最小的这种题,比如这道就是让区间移动最大的最小,也就是所有区间的移动距离都小于等于这个最小值。很明显是有贪心的成分,如果我们正着贪心,通过选哪个区间先移动来求得这个最小值考虑的情况太多。那我们枚举二分答案,假设最小值是x,然后我们算每个区间在这个移动幅度内能否覆盖整个要覆盖的范围,能得话就记录下答案,并且继续在左半部分二分寻求更小的答案。首先要将区间按右端点升序排列,这样贪心的原因,我的理解是这样,既然我们的已经从零覆盖到的最大位置,是从左开始一直向右扩展的,那么假设每个区间大小一样,那么怎么移动才能让所有的区间移动区间尽可能小呢?显然是按右端点从左向右移动覆盖,这样第一个区间移动后(假设左移),那么这个已经覆盖的最大位置就会向右移动,我们每次循环只要找到一个合适的区间就停止,在进行下一轮循环,直到这一轮循环一个符合条件的都找不到,因为一旦找到一个,这个已经覆盖的最大位置就变了,那么之前有可能有的通过移动x无法覆盖到最大位置的区间离新的最大位置更近了,而继续循环将无法循环到这个曾经不符和条件的区间。

代码;
 

#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
int maxx=20000;
int n;
struct node
{
    int l;
    int r;
} no;
bool cmp(node a,node b)
{
    if(a.r==b.r)return a.l<b.l;
    return a.r<b.r;
}
vector<node>v;
int check(int x)
{
    vector<node>w(v);

    int last=0;
    while(true){
            int fu=0;
    for(int i=0; i<w.size(); i++)
    {int lenn=w[i].r-w[i].l;
      if(w[i].l-x<=last&&w[i].r+x>=last){
          fu=1;
        if(w[i].l+x>=last){
            last+=lenn;
        }
        else{
            last+=(w[i].r+x-last);
        }
        w.erase(w.begin()+i);//找到一个就删一个
        break;//只要找到一个就退出循环,因为last已经更新的,也就是往右移动了,需要重新从剩下的区间中再次择优选择。(关键之处2)
      }
    }
      if(w.size()==0||fu==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);
            no.l=ll*2;//为了不让移动的最小距离出现小数,所以所有区间都乘二,因为最小的移动距离就是两个区间相差一,两边区间各移动0.5,成二后就变成了1,覆盖的区间也就变成了0-20000
            no.r=rr*2;
            v.push_back(no);
        }
        sort(v.begin(),v.end(),cmp);
        int l=0,r=20000;
        double ans=0;
        while(l<=r)//(关键之处1)
        {
            int mid=(l+r)/2;
            if(check(mid))//符合条件就继续找更小的
            {
                r=mid-1;
                ans=mid;
            }
            else
            {
                l=mid+1;
            }
        }
        ans/=2.0;
 cout<<ans<<endl;

        // printf("%d% d %d %d",v[0].l,v[0].r,v[1].l,v[1].r);
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值