• 区间移位


    问题描述
      数轴上有n个闭区间D1,…,Dn。其中区间Di用一对整数[aibi]来描述,满足ai < bi。已知这些区间的长度之和至少有10000。所以,通过适当的移动这些区间,你总可以使得他们的“并”覆盖[0, 10000]——也就是说[0, 10000]这个区间内的每一个点都落于至少一个区间内。
      你希望找一个移动方法,使得位移差最大的那个区间的位移量最小。
      具体来说,假设你将Di移动到[ai+cibi+ci]这个位置。你希望使得maxi |ci|  最小。
    输入格式
      输入的第一行包含一个整数n,表示区间的数量。
      接下来有n行,每行2个整数ai,  bi,以一个空格分开,表示区间[aibi]。保证区间的长度之和至少是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。
     
    区间移位,最小是移动0.5,这个可以很容易想到,因为给出的区间端点都是整数,要使得移位最小,显然是要平分的比如两个区间之间空了1,彼此相对移动0.5,就可以填补。
    所以首先所有的数据都乘以2,最后的结果再除以2即可。
    要确定移动区间的大小,可以用二分来一点一点的逼近最小的移动标准,给定一个标准,判断是否可行。需要先对所有的区间进行排序,这里对区间的右端点升序排序,为啥不按左端点排序,如果按照左端点排序,可能产生错误答案,比如样例
    3
    0 1000
    100 900
    1100 10000
    显然把第二个区间左移100,第一个右移100即可,如果是按照左端点升序排序,也就是给定的顺序,需要移动200才能满足,这是不对的,所以按右端点排序,但是右端点排序后可能出现误判,因为我们每到一个区间,就要判断这个区间的左端点与前面已经合并的区间最右点的关系,比如说左边已经合并的区间的右端点是100,然后我们按照右端点排序的两个区间有1000 2000和100 2100,第一个区间不需要移动的,也就是说如果给出的移动标准低于1000,可能就会误判成不可行,但是实际是可行的,所以for循环里加了个while循环寻找后面的左端点考前的区间,然后标记使用过,下次不再使用。
    代码:
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pa;
    pa l[10001];
    int n;
    bool cmp(pa a,pa b) {
        if(a.second == b.second)return a.first < b.first;
        return a.second < b.second;
    }
    bool check(int d) {
        bool vis[100001] = {false};
        int last = 0,j = 0;///已经合并区间的最右端点
        for(int i = 0;i < n && last < 20000;i += (j == i),j = i) {
            while(j < n && (vis[j] || l[j].first - last > d)) j ++;///找到满足当前标准的左端点靠前的区间
            if(j >= n) return false;
            vis[j] = true;
            if(last - l[j].first >= d) last = max(last,l[j].second + d);///重叠部分比d大,最多往右移动d
            else {///新区间接在了last后面
                last += l[j].second - l[j].first;
            }
        }
        return last >= 20000;
    }
    int main() {
        scanf("%d",&n);
        for(int i = 0;i < n;i ++) {
            scanf("%d%d",&l[i].first,&l[i].second);
            l[i].first *= 2;
            l[i].second *= 2;
        }
        sort(l,l + n,cmp);
        int l = 0,r = 20000;
        while(l < r) {//二分寻找最合适标准
            int mid = (l + r) / 2;
            if(check(mid)) r = mid;
            else l = mid + 1;
        }
        if(l % 2) printf("%.1f",l / 2.0);
        else printf("%d",l / 2);
        return 0;
    }
  • 相关阅读:
    POJ1995 ZOJ2150 Raising Modulo Numbers【快速模幂】
    POJ3641 UVA11287 HDU1905 Pseudoprime numbers【素数判定+快速模幂】
    ACM题解系列之三:秋田拓哉:《挑战程序设计竞赛》(第2版)
    HDU3257 Hello World!【打印图案+位运算】
    ACM题解系列之二:刘汝佳:《算法竞赛入门经典训练指南》
    UVa10881 Piotr's Ants【模拟】
    POJ1852 UVa10714 Ants【水题】
    剑指Offer——平衡二叉树
    剑指Offer——二叉树的深度
    剑指Offer——数字在排序数组中出现的次数
  • 原文地址:https://www.cnblogs.com/8023spz/p/10721041.html
Copyright © 2020-2023  润新知