luogu2678 跳石子 二分

题目大意:

一个赛道上除起点、终点外有(N)个点。现要求你从中删除(M)个点,使得剩余点序列中相邻的点的最小值最大。求这个最大的最小值。

思路

我们最容易想到的算法便是:对序列从头到尾循环(M)次,每次把间距最小的一对点删除其中一个,删除的那一个点到两边的点的距离之和应当是相对小的那一个。但是代码具体实现怎么做?而且时间复杂度也太高了!此时我们应当直接换切入点,而不是想方设法在这个思路上寻找解决方法。
题中要我们求的是,而值的范围由起点和终点不改变已经是有限了。而且显然要求的这个最小值越大,要删去的点不变或越多,否则不变越少。于是我们就可以对要求的最大的最小值进行UpperBound二分了。
那么对于一个潜在的值mid,如何得知要想使剩余点序列中相邻的点的最小值至少为(m)要删去多少个点呢?问题就在于,如果我们知道一对相邻点距离小于mid,我们是删除左面的点还是右面的点。贪心告诉我们删除右面的点,因为删除右边的点r会影响到更右边的点r',它若不删去r则它与相邻的点的距离小于mid,那么它更可能不用删去它本身而满足条件,而删除左面的点达不到这样的效果。

//#define _DEBUG//DELETE!!!!!!!!!!!!!!!

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAX_P = 50010;
int P[MAX_P];
int N, M, L;

bool CanDelInM(int x)
{
    int lastPos = 0, cnt = 0;
    for(int i=1; i<=N+1; i++)
    {
        if(P[i] - lastPos < x)
            cnt++;
        else
            lastPos = P[i];
    }
    return cnt <= M;
}


int UpperBound(int l, int r, bool (*GreaterOrEqual)(int))
{
    while(r > l)
    {
        int mid = (l + r + 1) / 2;
        if(GreaterOrEqual(mid))
            l = mid;
        else
            r = mid - 1;
    }
    return l;
}

int main()
{
    scanf("%d%d%d", &L, &N, &M);
    for(int i=1; i<=N; i++)
        scanf("%d", P + i);
    P[N+1]=L;
    sort(P+1, P+N+1);
    printf("%d
", UpperBound(1, L, CanDelInM));
    return 0;
}

原文地址:https://www.cnblogs.com/headboy2002/p/9094533.html