题目描述:
这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。
为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能移走起点和终点的岩石)。
输入:
第一行包含三个整数 L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。保证 L≥1 且 N≥M≥0。
接下来 N 行,每行一个整数,第 i 行的整数 Di(0<Di<L), 表示第 i 块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。
输出:
一个整数,即最短跳跃距离的最大值。
思路:
二分整体思路参照的是这篇博客跳石头,这里详细讲解了二分过程,以及这道题的思路,写的非常详细!!
暴力解显然是会超时的,一般对于这种“最大值最小”或者“最小值最大”的题目,应该就满足二分答案的有界性和单调性,这就是二分法需要满足的条件。那么如何二分查找呢?
- 直接二分跳跃距离,把这个值作为当前的解,然后用一个judge函数判断当前解是否是可行解(最优解一定是可行解,可行解不一定是最优解),如果是可行解,那么就去右边继续找,因为区间是递增的,需要求的是最大的最小距离,所以要往右去寻找可能会存在的更优的解。如果不是可行解,那么就去左边寻找。
- 最主要的就是judge函数,可以先来模拟跳跃过程,例如当前在0这个点,那么我跳到第1个点的时候需要判断这两点间距离是否大于等于我当前这个可行解,如果大于等于,那我就直接跳过去,反之,小于的话我就应该把这块石头搬走,移动石头数加一,继续往前跳。需要注意的是,要跳到n+1,因为题上说了n块石头不包括起点和终点。
- 模拟完这个过程之后,用需要搬走的石头数和限制搬走的石头数m比较,如果大于m肯定不行,如果小于等于m,则是合法的(小于的情况,我可以多搬走几块石头,只要保证我的最小距离不变即可)
下面是代码:
#include <iostream>
using namespace std;
//二分需要满足两个条件。一个是有界,一个是单调。
int d,n,m;//起点到终点的距离,起点和终点之间的岩石数,组委会至多移走的岩石数
int ans,mid;
int a[50005];
bool judge(int x)//x为当前二分的解,判断是否合法
{
int sum=0;//记录解为x时,需要移走石头的数量
int i=0;//下一块石头的编号
int now=0;//当前人在的位置
while(i<n+1)
{
i++;
if(a[i]-a[now]<x)//两个位置间距离小于x,那么这块石头就要搬走
sum++;
else
now=i; //大于x,就直接跳到这个位置
}
if(sum>m)
return false;
else
return true;
}
int main()
{
cin>>d>>n>>m;//N 块岩石(不含起点和终点的岩石)
for(int i=1;i<=n;i++)
cin>>a[i];
a[n+1]=d;//n不是终点
int l=1;//左边界
int r=d;//右边界
while(l<=r)
{
mid=(l+r)/2;
if(judge(mid))//判断当前解是否为可行解
{
ans=mid;
l=mid+1;//如果mid是可行解,那么右边可能会有更优的解
}else
{
r=mid-1;//mid不是可行解,那么就去左边寻找
}
}
cout<<ans<<endl;
return 0;
}