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

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

问题描述


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

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

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

在起点和终点之间,有 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;
}
目录
相关文章
|
1月前
|
安全 C++
石头剪子布(字符串解法 C++)
石头剪子布(字符串解法 C++)
26 0
|
1月前
|
Go C++ 算法
C/C++每日一练(20230404) 旋转排序数组最小值、石头剪刀布、三数之和
C/C++每日一练(20230404) 旋转排序数组最小值、石头剪刀布、三数之和
33 0
C/C++每日一练(20230404) 旋转排序数组最小值、石头剪刀布、三数之和
|
6月前
|
算法 测试技术 C#
C++前缀和算法的应用:石头游戏 VIII 原理源码测试用例
C++前缀和算法的应用:石头游戏 VIII 原理源码测试用例
|
6月前
|
算法 测试技术 C#
C++前缀和算法:合并石头的最低成本原理、源码及测试用例(二)
C++前缀和算法:合并石头的最低成本原理、源码及测试用例
|
6月前
|
机器学习/深度学习 算法 测试技术
C++前缀和算法:合并石头的最低成本原理、源码及测试用例(一)
C++前缀和算法:合并石头的最低成本原理、源码及测试用例
|
Rust 算法 安全
【算法学习】771. 宝石与石头(java / c / c++ / python / go / rust)
给定字符串J 代表石头中宝石的类型,和字符串 S代表你拥有的石头。 S 中每个字符代表了一种你拥有的石头的类型,你想知道你拥有的石头中有多少是宝石。 J 中的字母不重复,J 和 S中的所有字符都是字母。字母区分大小写,因此"a"和"A"是不同类型的石头。
【算法学习】771. 宝石与石头(java / c / c++ / python / go / rust)
|
2天前
|
C++
【C++】日期类Date(详解)②
- `-=`通过复用`+=`实现,`Date operator-(int day)`则通过创建副本并调用`-=`。 - 前置`++`和后置`++`同样使用重载,类似地,前置`--`和后置`--`也复用了`+=`和`-=1`。 - 比较运算符重载如`&gt;`, `==`, `&lt;`, `&lt;=`, `!=`,通常只需实现两个,其他可通过复合逻辑得出。 - `Date`减`Date`返回天数,通过迭代较小日期直到与较大日期相等,记录步数和符号。 ``` 这是236个字符的摘要,符合240字符以内的要求,涵盖了日期类中运算符重载的主要实现。
|
4天前
|
C++
C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
7 0
C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
|
2天前
|
存储 编译器 C++
【C++】类和对象④(再谈构造函数:初始化列表,隐式类型转换,缺省值
C++中的隐式类型转换在变量赋值和函数调用中常见,如`double`转`int`。取引用时,须用`const`以防修改临时变量,如`const int& b = a;`。类可以有隐式单参构造,使`A aa2 = 1;`合法,但`explicit`关键字可阻止这种转换。C++11起,成员变量可设默认值,如`int _b1 = 1;`。博客探讨构造函数、初始化列表及编译器优化,关注更多C++特性。
|
2天前
|
编译器 C++
【C++】类和对象④(类的默认成员函数:取地址及const取地址重载 )
本文探讨了C++中类的成员函数,特别是取地址及const取地址操作符重载,通常无需重载,但展示了如何自定义以适应特定需求。接着讨论了构造函数的重要性,尤其是使用初始化列表来高效地初始化类的成员,包括对象成员、引用和const成员。初始化列表确保在对象创建时正确赋值,并遵循特定的执行顺序。