蓝桥杯_跳石头

题目描述

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

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

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

输入描述

输入文件第一行包含三个整数 L,N,ML,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。

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

其中,0≤M≤N≤5×104,1≤L≤1090≤M≤N≤5×104,1≤L≤109。

输出描述

输出只包含一个整数,即最短跳跃距离的最大值。

示例

输入

25 5 2
2
11
14
17
21

输出

4

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M

思路

由题目中的让选手的最短跳跃距离尽可能长,可以考虑用二分法进行求解。

一块块的去遍历石头,最后肯定会超时。所以不妨我们去猜这个最短跳跃距离d。

代码思路

首先,依照题意读入数据。

然后,还需定义一个stone数组,大小为N + 1,索引0处存起点,其余N块存放每块石头距离原点的距离。

下面使用二分法进行d的寻找:

该题二分法是用来猜最短跳跃距离d的,而d的范围是[0,L],所以我们定义变量l = 0,r = L;然后用二分的板子(我用的是l <= r那版)。

那么依据什么标准去调整l,r的大小呢?

因为组委会最多只能移走M块石头,所以我们声明一个变量num——在当前猜测的最短跳跃距离d的前提下,组委会需要移走的石头数。因为要让最短跳跃距离d最大,所以移走的石头数num应该尽可能的多,这样河道中的石头数就少(长度相同,河中石头少了,自然最短的跳跃距离就会变大),在不超过M的前提下。

那么如何去计算num等于多少呢?

我们就需要用到前面定义的stone数组,大小为N + 1,索引0处存起点,其余N块存放每块石头距离原点的距离。

我们将stone数组从索引为1处开始遍历,每遍历一次,我们就判断stone[i] - pos 是否小于我们猜测的最短跳跃距离d,如果小于的话,说明这个石头就可以移走(因为最短跳跃距离d已经是当前河道跳跃距离中最大的了),同时将num ++;如果大于d的话,当前的石头就不能移走,同时把pos变到当前的石头处stone[i]。

当stone数组都遍历完成后,num的值就可以确定了,如果num <= M,返回true,就说明搬走的石头数小于M,即河道中的石头还能再搬走几个,所以在二分板子中,我们还应加大距离d,即l = m + 1,同时定义变量ans记录当前的m,当前的m其实是满足条件的,但是不是最短的跳跃距离而已。如果num > M,我们就返回false,说明石头搬走的太多了,导致河道中的最短跳跃距离太大了,所以减小d的取值,即r = m - 1;

最后,当二分的循环结束的时候,答案已经被存在了ans中,直接输出即可。

代码

import java.util.Scanner;

public class 跳石头 {
	
	static int L,N,M;
	static int[] stone;
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		L = sc.nextInt();
		N = sc.nextInt();
		M = sc.nextInt();
		stone = new int[N + 1];
		for(int i = 1;i <= N;i ++) {
			stone[i] = sc.nextInt();
		}
		int l = 0;
		int r = L;
		int ans = 0;
		while(l <= r) {
			int m = (r + l) >>> 1;
			if(check(m)) {	// m小了
				l = m + 1;
				ans = m;
			}
			else {
				r = m - 1;
			}
		}
		System.out.println(ans);
	}
	public static boolean check(int 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) {		// 可以搬走的石头数小于最大值M => 参数d小了
			return true;
		}
		else {		// 可以搬走的石头数大于最大值M => 参数d大了
			return false;
		}
	}
}

  • 31
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值