【C++题解】NOIP2015提高组 - 跳石头

文章介绍了如何解决一项名为跳石头的编程竞赛问题,其中涉及到在有限的预算内移除岩石以增加比赛难度,要求找到使选手跳跃距离最长的方案。通过二分搜索和贪心算法,找到最短跳跃距离的最大值,确保移除不超过M块岩石。

✍个人博客:https://blog.csdn.net/Newin2020?spm=1011.2415.3001.5343
📚专栏地址:杂题讲解
📝原题地址:跳石头
📣专栏定位:在这里我将整理一些其他比赛或面试的题解~
❤️如果有收获的话,欢迎点赞👍收藏📁,您的支持就是我创作的最大动力💪

问题描述

一年一度的“跳石头”比赛又要开始了!

这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。

组委会已经选择好了两块岩石作为比赛起点和终点。

在起点和终点之间,有 N 块岩石(不含起点和终点的岩石)。

在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。

为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。

由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能移走起点和终点的岩石)。

输入格式

输入文件第一行包含三个整数 L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。

接下来 N 行,每行一个整数,第 i 行的整数 Di(0 < Di < L) 表示第 i 块岩石与起点的距离。

这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。

输出格式

输出文件只包含一个整数,即最短跳跃距离的最大值。

数据范围

0≤M≤N≤500000
1≤L≤109

输入样例:
25 5 2 
2
11
14
17 
21
输出样例:
4

思路

这道题是二分套路题中的经典问题 —— 最小值最大化,除此之外还有最大值最小化问题,解法和这个差不太多。

我们先来考虑暴力法如何做,需要枚举 n 块石头中选取 m 个石头的所有组合,时间复杂度直接爆炸!

所以需要优雅一点的暴力,同样是枚举,不过我们枚举的是最短距离的最大值 d。对这个临界值进行二分,每次二分都去判断当前最短距离的最大值是否能通过移走 m 个以内的石头达到中位值 mid,如果满足条件,则使 left=mid,反之使 right=mid-1,因为我们要使最小值尽可能的大,所以当 mid 已经满足条件时我们任然要继续往更大的值进行尝试,另外 mid 如果此时已经满足条件则有可能就是最终答案,所以需要用 left 保存下来。

另外,我们 check 中可以采用贪心思想:

  • 如果当前石头和上一块石头的距离小于 len,则将当前石头拿走。这是因为如果某个最优解中是拿走了上一块石头,那么我们可以改成留下上一块石头,拿走当前这块石头,这样总共拿走的石头数量不变,所以当前选法也是一组最优解。
  • 如果当前石头和上一块石头的距离大于等于 len,则将上一块石头更新成当前这块。

扫描结束后观察拿走的石头数量是否小于等于 m,如果小于等于则满足条件。

代码

#include<cstdio>
int len, n, m;
int stone[50005];
bool check(int d) {   //检查距离d是否合适
    int num = 0;       //num记录搬走石头的数量
    int pos = 0;       //当前站立的石头
    for (int i = 1; i <= n; ++i)
        if (stone[i] - pos < d)  num++;          //第i块石头可以搬走
        else                  pos = stone[i];   //第i块石头不能搬走
    if (num <= m) return true;  //要移动的石头比m少,满足条件
    else return false;         //要移动的石头比m多,不满足条件
}
int main() {
    scanf("%d%d%d", &len, &n, &m);
    for (int i = 1; i <= n; ++i) scanf("%d", &stone[i]);
    stone[++n] = len;
    int L = 0, R = len, mid;
    while (L < R) {
        mid = L + (R - L + 1) / 2;
        if (check(mid)) L = mid;  //满足条件
        else           R = mid - 1;  //不满足条件,说明mid大了,调小一点
    }
    printf("%d\n", L);
    return 0;
}
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值