ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

跳石头 解题报告【二分答案】

2021-01-04 14:00:28  阅读:273  来源: 互联网

标签:二分 移走 终点 int mid 石头 解题 check


跳石头


P2678 [NOIP2015 提高组] 跳石头
好,我又来了,之前写了一个同样是 二分答案的题目, 吃萝卜,大家可以康康。

吃萝卜 解题报告【二分答案】

题目

描述

一年一度的“跳石头”比赛又要开始了!
这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终点的岩石) 。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。
为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能移走起点和终点的岩石)。

输入

第一行包含三个整数L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。保证 L≥1且N≥M≥0。
接下来 N 行,每行一个整数,第 i 行的整数 Di(0<Di<L), 表示第 ii 块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。

输出

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

讲解

先吐后讲,是好习惯。
组委会的问题关我何事,是少了一只脚还是少了一只手
咳咳,现在开始正片

分析题目

1.选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。
2.使得选手们在比赛过程中的最短跳跃距离尽可能长
3.组委会至多从起点和终点之间移走 M 块岩石(不能移走起点和终点的岩石)。
这两点便是题目最重要的地方。
因此可以看出,此题的用意是让我们查找到一个合适的数,成为最大值,并且让这个最小值最大

“最小值最大”,我们最常用同样也是二分答案。之后小编会写一篇关于二分答案的博客,希望各位看看 (^ _ ^)

重点,敲黑板

这道题跟之前的吃萝卜有一些相同也有一些不同。
1.这道题同样是 “相邻的”(即需要一段连续的)
2.这里选择的次数也变成了固定的m
所以在吃萝卜的代码的前提下修改一下;

分解代码讲解

二分上下界的取值

首先,我们要确定一下二分上下界的取值(重点,最让人头疼的地方)
这次先找r。
从题目“0<Di<L。”可以得知,r应直接取L(此处L指总长),因为我们是找最大的最小值,无论如何都不会大于L因为哪怕把起点和终点中的所有石头都去掉,距离也不会大于L。,所以我们直接把r取L即可。

找完r,后我们找l。
因为我们是找最大的最小值,而题目数据范围指出L≥1,且0<Di<L,故直接取1即可。

    for(int i=1;i<=n;i++)// 一 共 有 n 块 石 头
	{
		cin>>a[i];     
	}
	int l=1,r=L;//r 取 总 共 长 度 L , l  直 接 取 1.

二分模板

这一步简单,我喜欢
一般情况下,这一步只要套模板即可,我个人比较倾向于第二套

    while(l<r)
	{
		int mid=(l+r)>>1; //取 L 和 R 的 平 均  数
		if(check(mid)) r=mid; //check一下mid,康康这个mid是否合法
		                      //如果合法,则选择左半部分,康康有没有更小的合法数
		else l=mid+1;         //如果不合法,则选择右半部分,寻找合法数
	}

check

最后只剩下check(判断)函数来判断mid是否合法了。
这个check怎么实现呢?check函数每个题有每个题的写法,但大体上的思想应该都是一样的——想办法检测这个解是不是合法。拿这个题来说,我们去判断如果以这个距离为最短跳跃距离需要移走多少块石头,先不必考虑限制移走多少块,等全部拿完再把拿走的数量和限制进行比对,如果超出限制,那么这就是一个非法解,反之就是一个合法解,很好理解吧。

int check(int mid)
{
	int num=0,sum=0;
	for(int i=1;i<=n;i++)
	{
		if(a[i]-sum<mid)num++;//判 断 如 果 以 这 个 距 离 为 最 短 跳 跃 距 离  需 要 移 走 多 少 块 石 头
		else sum=a[i];
	}
	if(num>m)return 0;//如 果 超 出 限 制 , 那 么 这 就 是 一 个 非 法 解 , 反 之 就 是 一 个 合 法 解
	else return 1;
}

总代码

#include<bits/stdc++.h>
using namespace std;
int n,m,L,a[50010];
int check(int mid)
{
	int num=0,sum=0;
	for(int i=1;i<=n;i++)
	{
		if(a[i]-sum<mid)num++;
		else sum=a[i];
	}
	if(num>m)return 0;
	else return 1;
}
int main()
{
	cin>>L>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	int l=1,r=L;
	while(l!=r)
	{
		int mid=(l+r+1)>>1;
		if(check(mid))l=mid;
		else r=mid-1;
	}
	cout<<l;
	return 0;
} 

写完啦,开森。
这道题洛谷上也有的P2678 [NOIP2015 提高组] 跳石头网上直接搜跳石头也是搜得到的。之所以就写了一篇,专门给像我这样的Gu Luo康康的,如果有大佬的话,希望找找茬。

点评

简要的说说这道题,要说难度,没有上一篇的吃萝卜难,同样,也没上一篇吃萝卜的经典。

如果是刚刚了解二分答案的人的话,我是会强推这道题的,但如果是想要深入了解的话就当练练手即可。
(以上皆是小编的个人看法,如果想法不同,可以在评论区提出)

标签:二分,移走,终点,int,mid,石头,解题,check
来源: https://blog.csdn.net/xiaoruihang/article/details/112007185

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有