【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;
}
目录
相关文章
|
7月前
|
安全 C++
石头剪子布(字符串解法 C++)
石头剪子布(字符串解法 C++)
67 0
|
7月前
|
Go C++ 算法
C/C++每日一练(20230404) 旋转排序数组最小值、石头剪刀布、三数之和
C/C++每日一练(20230404) 旋转排序数组最小值、石头剪刀布、三数之和
50 0
C/C++每日一练(20230404) 旋转排序数组最小值、石头剪刀布、三数之和
|
算法 测试技术 C#
C++前缀和算法的应用:石头游戏 VIII 原理源码测试用例
C++前缀和算法的应用:石头游戏 VIII 原理源码测试用例
|
算法 测试技术 C#
C++前缀和算法:合并石头的最低成本原理、源码及测试用例(二)
C++前缀和算法:合并石头的最低成本原理、源码及测试用例
|
机器学习/深度学习 算法 测试技术
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)
|
1月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
51 2
|
1月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
106 5
|
1月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
97 4
|
1月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
116 4