Loading

蓝桥杯省赛 区间移位(二分+玄学贪心)

问题描述
  数轴上有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。

看到最大值最小很容易想到二分答案区间转化为判定,关键在于check函数怎么写。可以想到,需要让移动距离最大为mid的前提下把整个0~10000的区间铺满肯定是要贪心地移动,类似区间覆盖问题。不妨从左往右进行覆盖,设置一个变量pos表示从0~pos这一段已经被覆盖,需要从没有使用过的剩下的区间里选一段。假设目前在判断第i段区间,这时可以分为三种情况:s[i].l(即当前区间左端点)在pos左边,等于pos和在pos右边。处于pos两边时又需要看移动距离在mid内是否能衔接上pos,具体讨论可以看代码相关部分。

接下来就是最麻烦的地方,我也是看了不少博客才过了这题,也不知道自己理解的对不对。一开始我直接按区间左端点sort了一遍,然后只遍历一遍结构体数组,只得了50分还是六十分来着…后来按右端点sort,再把pos初始化为0,还是只遍历一遍结构体数组,竟然改到了90分…大佬给的解释是因为是从左边开始找的,所以按右端点排序,这样移动的较少就可以覆盖的更多。我觉得可依据这么一个例子:一个区间长为m,一个为n且m>n同时n这一段包含在m里面,如果按左端点排序的话会优先考虑使用m,似乎不太划算,相当于浪费长区间了;按右端点排序的话n不能满足衔接的要求才调用m,更合理一些。其次要注意的是,肯定不能只遍历一遍结构体数组,这样会有遗漏,因此用while循环嵌套一个for循环,最终复杂度是O(n^2logn)能过掉这题。还有一点就是题目中输出的答案可能有小数可能有整数,这其实很简单,因为区间端点全都是整数,所以mid要么是整数,要么就是0.5(两个区间同时凑出1),不可能有其他的比如0.3和0.7凑(整数端点移不出来),所以直接把端点*2,0~10000改成0~20000,输出的时候/2即可。

#include <bits/stdc++.h>
using namespace std;
int n;
struct section
{
    int l;
    int r;
}s[10005];
bool cmp(section a,section b)
{
    if(a.r!=b.r)return a.r<b.r;
    else return a.l<b.l;
}
bool check(int mid)
{
    int i;
    int pos=0;//pos记录当前已经填好的区间的最末端 
    bool vis[10005]={0};
    while(1)
    {
        bool find=false;
        for(i=1;i<=n;i++)
        {
            if(vis[i])continue;
            if(s[i].l>pos)//当前区间需要往左靠拢 
            {
                if(s[i].l-pos>mid)//这一段不能满足 
                {
                    find=false;
                }
                else
                {
                    pos=s[i].r-(s[i].l-pos);
                    vis[i]=1;
                    find=true;
                }
            }
            else if(s[i].l==pos)
            {
                pos=s[i].r;
                vis[i]=1;
                find=true;
            }
            else
            {
                if(pos-s[i].l<=mid)
                {
                    pos=s[i].r+(pos-s[i].l);
                    vis[i]=1;
                    find=true;
                }
                else
                {
                    if(s[i].r+mid>pos)
                    {
                        pos=s[i].r+mid;
                        vis[i]=1;
                    }
                    find=true;
                }
            }
        }
        if(pos>=20000)return true;
        if(!find)break;
    }
    //这里别忘记改 
    return false;
}
int main()
{
    int i;
    cin>>n;
    for(i=1;i<=n;i++)
    {
        scanf("%d%d",&s[i].l,&s[i].r);
        s[i].l*=2;
        s[i].r*=2;
    }
    sort(s+1,s+n+1,cmp);
    int left=0,right=20000,mid,ans=0;
    while(left<right)
    {
        mid=(left+right)/2;
        if(check(mid))
        {
            right=mid;
        }
        else left=mid+1;
    }
    ans=left;
    if(ans%2==0)
    {
        cout<<ans/2;
    }
    else
    {
        double aans=ans/2.0;
        printf("%.1lf",aans);
    }

    return 0;
}

 

 
posted @ 2020-03-06 23:10  脂环  阅读(660)  评论(0编辑  收藏  举报