码迷,mamicode.com
首页 > 编程语言 > 详细

C++跳石头题解(二分答案)

时间:2020-08-28 15:02:14      阅读:73      评论:0      收藏:0      [点我收藏+]

标签:through   数组   line   函数   i+1   注意   输入输出   限制   st表   

跳石头

描述

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

这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。

为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M块岩石(不能移走起点和终点的岩石)。

输入

第一行包含三个整数 L,N,M分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。保证L≥1 且N≥M≥0。

接下来 N行,每行一个整数,第 i行的整数Di?(0<Di?<L), 表示第 i 块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。

输出

一个整数,即最短跳跃距离的最大值。

输入样例 1 

25 5 2

2

11

14

17

21

输出样例 1

4

提示

输入输出样例 1 说明:将与起点距离为 2和 14的两个岩石移走后,最短的跳跃距离为 4(从与起点距离 17 的岩石跳到距离 21 的岩石,或者从距离 21 的岩石跳到终点)。

另:对于20%的数据,0 ≤ M ≤ N ≤ 100≤M≤N≤10。

对于50%的数据,0 ≤ M ≤ N ≤ 1000≤M≤N≤100。

对于 100%的数据,0 ≤ M ≤ N ≤ 50000,1 ≤ L ≤ 1000000000。

 

来源

NOIP2015 提高 t1

  先看数据范围,L居然达到了惊人的10的8次方,所以我们必须使用一种效率高、耗时少的算法——没错,就是二分(今天学的就是二分啊)。二分算法每次都会把数据分成两半,所以其时间复杂度只有logL。反观暴力枚举,其时间复杂度为L的平方。所以对于这道题来说,二分肯定是最合理的算法。

  既然要使用二分,那就必须决定要分什么。分移走的石头位置?没有任何用处。所以唯一剩下的能分的值就是本道题的答案:最短跳跃距离的最大值所以我们不妨沿着这条思路走下去。

  我们可以首先使用二分求出最短跳跃距离的最大值,然后检查其是否合法,这里可以使用一个bool函数ok来判断,二分那部分都是老套路了,bool函数ok才是二分的灵魂:

bool ok(int x)//x为通过二分算出来的最短跳跃距离的最大值
{
    int tot=0;//tot表示需要移走多少块石头
    int last=0;//last表示上一个石头的位置
    for(int i=1;i<=n;i++)//枚举每个石头
    {
        if(d[i]-last<x) tot++;//d[i]-last表示这个石头到上个没被移走的石头/起点的距离,如果距离过小,就需要移走此块石头,这样剩下的距离就是last到d[i+1]
        else last=d[i];//更新last信息,为下次循环做准备
    }
    return tot<=m;//如果移走石头的个数小于等于m,则x的值是可取的
}

解决了ok函数,剩下的部分都是二分老套路,只有r的取值需要注意一下

完整代码+注释如下:

#include<bits/stdc++.h>
using namespace std;
int d[500010],L,n,m,l,r;//注意数组不要开小了

bool ok(int x)//x为通过二分算出来的最短跳跃距离的最大值
{
    int tot=0;//tot表示需要移走多少块石头
    int last=0;//last表示上一个石头的位置
    for(int i=1;i<=n;i++)//枚举每个石头
    {
        if(d[i]-last<x) tot++;//d[i]-last表示这个石头到上个没被移走的石头/起点的距离,如果距离过小,就需要移走此块石头,这样剩下的距离就是last到d[i+1]
        else last=d[i];//更新last信息,为下次循环做准备
    }
    return tot<=m;//如果移走石头的个数小于等于m,则x的值是可取的
}

int main()
{
    cin>>L>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>d[i];
    r=L;//二分的右边界一定不会超过河道的长度,如果取一个极大值也没有问题,但取L能节省一些时间
    while(r-l>1)//二分求最短跳跃距离的最大值
    {
        int mid=(l+r)/2;
        if(ok(mid))
            l=mid;
        else
            r=mid;
    }
    if(!ok(r)) r=l;//扫尾工作
    cout<<r;//输出答案
    return 0;
}

perfect!

 

 

C++跳石头题解(二分答案)

标签:through   数组   line   函数   i+1   注意   输入输出   限制   st表   

原文地址:https://www.cnblogs.com/YC2022/p/13556203.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!