题目链接

https://www.dotcpp.com/oj/problem1846.html

解题思路

二分+贪心。
大思路比较好想,但是贪心思路不好想。
二分移动的距离;
按照区间右端点从小到大排序,每次都先尝试插入未插入中右端点最小的。若此移动距离能让覆盖满区间,此距离可以;反之,不行。
另外值得注意的点:因为存在小数的情况,但是把二分换成double会严重影响复杂度,所以我们先把输入区间的左右端点乘2后再插入,最后输出的时候只需要除2即可。

AC代码

#include<bits/stdc++.h>
#define sc(x) scanf("%d",&x)
#define pr(x) printf("%d",x)
using namespace std;
const int B=20000;
typedef pair<int,int> P;
multiset<P> s;
int check(int mid){
    multiset<P> tmp(s);
    int now=0,flag=0;
    while(!tmp.empty()){
        for(multiset<P>::iterator it=tmp.begin();it!=tmp.end();it++){
            flag=0;//用于记录tmp是否还有可以用来覆盖的区间 
            int tta=(*it).second,ttb=(*it).first;
            if(tta-mid<=now && ttb+mid>=now){//可以移动到跨越now的状态 
                flag=1;
                int len=ttb-tta;
                if(tta+mid>=now) now+=len;
                else now=ttb+mid;
                tmp.erase(it);
                break;//每次必须从tmp的头开始试着往区间里塞 
            }    
        }
        if(now>=B) return 1;
        if(!flag) return 0;
    }
    return 0;
}
int main(){
    int n,a,b,l=0,r=B;
    double ans;
    sc(n);
    for(int i=1;i<=n;i++){
        sc(a);sc(b);
        s.insert(P(b*2,a*2));
    }
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(mid)) r=mid-1,ans=mid;
        else l=mid+1;
    }

    cout<<ans*1.0/2.0;
}