洛谷--P2678--跳石头--二分法

题目描述:

这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 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;
} 

猜你喜欢

转载自blog.csdn.net/weixin_45102820/article/details/114486919