noip模拟赛 读

分析:感觉很像是贪心,但是直接贪找不到方法.一个暴力的想法是枚举最小步数,然后看每个指针能够覆盖到的位置,看看能不能覆盖到所有点.这个求最大覆盖就有点贪心的思想,因为给的ai,bi都是递增顺序的,考虑第i个指针,如果左边还有点没有覆盖到是一定要去覆盖的,剩下的步数可以一直往右走.这样的话有两种情况,要么是先把左边的覆盖了再往右走,要么是先尽量往右走,之后返回刚好把最左边的那个点给覆盖,讨论一下就可以了.如果左边没有点要覆盖,i尽量往右走就可以了.最后看被覆盖的指针是不是有m个即可.

      由于ai,bi非常大,枚举最小步数要消耗太多的时间,需要进行优化.仔细揣摩一下这个过程就会发现这很像二分+check,因为枚举最小步数是从小到大枚举的嘛,所以符合单调性,二分即可.

枚举答案并判断是否可行的暴力做法可以优化为二分.

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long ll;

int n, m;
ll a[100010], b[100010], ans;

bool check(ll x)
{
    ll j = 1;
    for (int i = 1; i <= n; i++)
    {
        if (a[i] - b[j] > x)
            return false;
        ll can = 0;
        if (b[j] < a[i])
        {
            ll rest = x - (a[i] - b[j]);
            can = max(b[j] + rest, a[i] + rest / 2);
            if (can < a[i])
                can = a[i];
        }
        else
            can = a[i] + x;
        while (j <= m && b[j] <= can)
            j++;
        if (j > m)
            return true;
    }
    return false;
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &a[i]);
    for (int i = 1; i <= m; i++)
        scanf("%lld", &b[i]);
    ll l = 0, r = 20000000010;
    while (l <= r)
    {
        ll mid = (l + r) >> 1;
        if (check(mid))
        {
            ans = mid;
            r = mid - 1;
        }
        else
            l = mid + 1;
    }
    printf("%lld\n", ans);

    return 0;
}

 

posted @ 2017-10-25 10:29  zbtrs  阅读(138)  评论(0编辑  收藏  举报