当前位置: 首页 > news >正文

基础算法之二分算法 --- 2

大家好,不同的时间,相同的地点,时隔多日我们又见面了。继上次的二分算法后,我们这次要来学习的是二分答案了。这个部分相较于前面的二分算法难度有相当的提升,希望大家有所准备。虽然难度增加了,但是博主还是会将其讲明白讲清楚的

let's go!

前言

二分算法虽然简单但是它是很重要的,在某些题目当中,我们以后还会遇到,希望大家能够有所重视。

1.二分答案

二分答案具体为:二分答案 + 判断

当做算法题的时候,如果遇到最大值最小以及最小值最大的问题,就可以思考着去使用二分答案来解决了。二分答案可以帮助解决绝大多数这类的问题,如果解空间在从小到大的变化过程中,判断答案的结果出现二段性,此时我们就可以去使用二分,去二分这个解空间,通过判断来找出最优解。

可能大家看这个概念还是比较抽象,跟着博主做完下面三道题后大家就会明白了。

2.题目

P2440 木材加工 - 洛谷

P1873 [COCI 2011/2012 #5] EKO / 砍树 - 洛谷

P2678 [NOIP 2015 提高组] 跳石头 - 洛谷

大家可以先行做一下这几道题,然后再看博主的思路。

3.木材加工

题目展示:

思路分析:

这道题可以说的上是二分答案的一道模版题目了,大家好好理解一下这道题:

大家看完题目后,应该有对题目有些理解了,那么我再来梳理一下这个题目吧。

这个题目会给出木头n个需要切割出来的木头段数k,然后会给出n个木头的长度a[i]

我们思考一下也就知道,如果我们将木头切割成1cm的木头(最短)那么我们肯定是可以满足题目要求的段数,但是问题来了我们切割成1cm的小段不是能够切割出来的最长?

那么我们一种暴力的解法就是:

暴力解法思路:

枚举切割的长度从1到最长的木头,然后开始通过这个切割长度x来计算段数(t) t = a[i] / x

通过将段数最后计算出来的和拿来和要求的k来比较,如果总和大于k就可以算是待正确答案。

这样的话,我们可以将切割长度从最长的木头长度往1开始减少的枚举,这样当第一次出现大于k的情况那个切割长度就是我们需要的答案

对于这个思路大家有兴趣可以去实现一下,难度不大。

但是会出现超时的情况,我们就得用到二分答案的算法来解决这道题了。

二分答案思路:

如果我们所要的切割长度为ans,我们无非就是要在0~maxlen中找到这个答案ans

------------------------------------------
[0                ans              maxlen]
------------------------------------------

当枚举的切割长度x在ans的左边时,切割出来的段数 t >= k

当枚举的切割长度x在ans的右边时,切割出来的段数 t < k

所以这道题答案是具有二段性的,所以可以通过二分来解决

left          mid             right
calc(mid)计算当前切割长度能切多少段 
calc(mid) >= k  left = mid;
calc(mid) <  k  right = mid - 1;

这时候后面的一个代码实现就变得简单很多了

代码实现:

#include <iostream>
using namespace std;
const int N = 1e5 + 10;
typedef long long LL;
LL n, k;
LL a[N];LL calc(LL x)
{//通过切割长度x计算切出的数量LL ret = 0;for (int i = 1; i <= n; i++){ret += a[i] / x;	}return ret;
}int main()
{cin >> n >> k;LL r = 0;for (int i = 1; i <= n; i++){cin >> a[i];r = max(r, a[i]);}LL l = 0;while(l < r){LL mid = (l + r + 1) / 2;if (calc(mid) >= k) l = mid;else r = mid - 1;}cout << l << endl;return 0;
}

4.砍树

题目展示:

思路分析:

这道题跟前面的那道题是很相似的大家看完前一道题后做这道题应该难度不会很大了。

但是现在还是跟着博主的思路开始分析一下这道题吧:

这道题给出了这一排树的数目n所需的木材m两个数,接着给出了每棵树的高度a[i]

我们需要找到一个合适的高度既能够保护树,也能满足需求的木材

那么我们的高度就只能是在0到最高的树的高度来枚举

如果我们最后答案的树高为ans

----------------------------------------------

[0                   ans                 maxh]

----------------------------------------------

当h小于ans时,获得的木材 l >= m

当h大于ans时,获得的木材 l < m

答案是具有二段性的,去思考使用二分答案来解决

left                 mid                 right

当枚举树高为mid,calc(mid)为此时获得的木材
当calc(mid) >= m时,l = mid
当calc(mid) < m时,r = mid - 1

代码实现:

#include <iostream>
using namespace std;
const int N = 1e6 + 10;
typedef long long LL;
LL n, m;
LL a[N];LL calc(LL x)
{// 计算出x树高的时候,切割出来的木材数LL ret = 0;for (int i = 1; i <= n; i++){if (a[i] >= x) ret += a[i] - x;}return ret;
}
int main()
{cin >> n >> m;LL r = 0;for (int i = 1; i <= n; i++){cin >> a[i];r = max(r, a[i]);}LL l = 0;while(l < r){LL mid = (l + r + 1) / 2;if (calc(mid) >= m) l = mid;else r = mid - 1;}cout << l << endl;return 0;
}

5.跳石头

题目展示:

思路分析:

这道题题目可能刚看会有些不明白是怎么一回事,可以仔细看看

现在跟着博主的思路开始分析一下这道题目吧:

题目中会给出起点到终点的距离L起点和终点之间的岩石数N,以及组委会至多移走的岩石数M

然后会给出N个值a[i]每个值a[i]代表第i个石头距离起点的距离

题目要我们求的是什么呢?让我们求最短跳跃距离的最大值

这是不是就是很符合我们二分答案吗?

那么什么叫做最短跳跃距离的最大值呢?

由于移走石头后选手会进行每块石头之间的跳跃,在所有跳跃中,会出现一个最短的跳跃距离

但是我们想要通过移走石头来使得选手这个最短跳跃距离达到最大

最终要求出这个最短跳跃距离。

对于最短跳跃距离x,和移走石头数c

当x增加,c也在增加

当x减小,c也在减少

-----------------------------------------------------------------

[1                                ans                                  L]

-----------------------------------------------------------------

当x <= ans时,移走的岩石小于等于 m

当x > ans时,移走的岩石c肯定大于 m (规定移走的岩石数)

left                        mid                       right

calc(mid)计算在最小跳跃距离为mid的时候,移走的石头数

calc(mid) <= m  在跳跃mid距离时,移走石头小于等于m  left = mid
calc(mid) >  m  在跳跃mid距离时,移走石头大于m  right = mid - 1

那么问题来了?如何计算当最小跳跃距离为mid的时候,移走的石头数呢?

这个地方我们可以通过双指针的方法来实现它:

一个指针i指向前一个石头,另外一个指针j向后查找,通过a[j] - a[i] < mid就让j++

当出现a[j] - a[i] >= mid就停止向后查找,因为根据题目可以知道向后会越来越大

然后通过i和j的下标计算出来中间移走的石头数量 j - i - 1

最后更新i的大小,i = j - 1 (因为在for循环里面还有一个i++) 让i更新指向j的位置

然后继续执行逻辑,最后得出这种情况的移走石头数量。

这样我们就可以去实现我们的代码了!

代码实现:

#include <iostream>
using namespace std;
const int N = 5e4 + 10;
typedef long long LL;
LL l, n, m;
LL a[N];
LL calc(LL x)
{//计算最短跳跃距离为x的时候,要移动的石头数LL ret = 0;// 记录石头数// 遍历石头 for (int i = 0; i < n; i++){int j = i + 1;while(j <= n && a[j] - a[i] < x) j++;ret += j - i - 1;i = j - 1;}return ret;
}
int main()
{cin >> l >> n >> m;for (int i = 1; i <= n; i++){cin >> a[i]; // 岩石距离起点距离 }a[n + 1] = l;//避免计算的错误n++; LL left = 1, right = l;while(left < right){LL mid = (left + right + 1) / 2;if (calc(mid) <= m) left = mid;else right = mid - 1;}cout << left << endl;return 0; 
}

结语

很高兴大家能够看到这里,希望前面内容的讲解能够给大家带来收获,弄懂二分答案的原理和使用场景,对于最后这道题可能比较难以理解,但是大家可以仔细思考思考。

如果这篇文章对您有所收获的话,请多多点赞 + 收藏 + 关注!


文章转载自:

http://Q2g7bTbC.sfsjh.cn
http://SKrk2hKY.sfsjh.cn
http://Fl2UazA5.sfsjh.cn
http://2bOznajs.sfsjh.cn
http://vt0pdRld.sfsjh.cn
http://b5hY64pp.sfsjh.cn
http://rpoSPh6l.sfsjh.cn
http://vQWCdwB0.sfsjh.cn
http://yAROFEh3.sfsjh.cn
http://Vngfz5x4.sfsjh.cn
http://CqDHSj3q.sfsjh.cn
http://oJ8BxNI2.sfsjh.cn
http://9BJjuZKv.sfsjh.cn
http://N7avp4ty.sfsjh.cn
http://eriMqBfO.sfsjh.cn
http://pyD3DNqF.sfsjh.cn
http://9g2KsdHL.sfsjh.cn
http://K8QKi7MQ.sfsjh.cn
http://ZM844zOM.sfsjh.cn
http://7QZg7mhC.sfsjh.cn
http://bACZwd0U.sfsjh.cn
http://9YXugNxx.sfsjh.cn
http://s4aaLDte.sfsjh.cn
http://vVQLYZAO.sfsjh.cn
http://bi8m4Ym5.sfsjh.cn
http://ajhWsbgS.sfsjh.cn
http://bAck33GW.sfsjh.cn
http://k7ZU9RD9.sfsjh.cn
http://Td9ZaE4g.sfsjh.cn
http://X1VVdQ2K.sfsjh.cn
http://www.dtcms.com/a/380695.html

相关文章:

  • Vue3+JS 复杂表单实战:从验证到性能优化的全流程方案
  • 基于RAG的智能客服系统
  • 建自己的Python项目仓库,使用工具:GitHub(远程仓库)、GitHub Desktop(版本控制工具)、VSCode(代码编辑器)
  • 容器使用卷
  • Vue3:根据el-input封装全局v-focus指令
  • 企业AI战略构建与成品选择指南
  • Semaphore和CountDownLatch
  • 实战ELK与AI MCP:构建高可用的智能化日志可观测体系
  • SAP-MM:SAP MM学习分享:深入浅出解析物料需求计划(MRP)及MRP配置图解
  • 【LLM】使用 Google ADK、Gemini、QDrant 和 MCP 构建深度研究系统
  • 【CSS学习笔记2】-css复合选择器
  • 186. Java 模式匹配 - Java 21 新特性:Record Pattern(记录模式匹配)
  • Electron下载失败
  • Origin绘制双Y轴网格叠加图|科研论文图表教程(附数据排列格式)
  • XXL-JOB框架SRC高频漏洞分析总结
  • 未启用Spring事务管理 执行mapper.xml文件的sql,为什么会自动提交
  • 亚马逊云代理:亚马逊云怎么样进行大规模数据分析与处理?
  • Linux防火墙iptables
  • 基于联邦学习与神经架构搜索的可泛化重建:用于加速磁共振成像|文献速递-最新医学人工智能文献
  • 如何将 Wine 应用包转换成玲珑格式包:完整技术教程
  • 函数库 动静态库
  • EPC企业如何通过数字化管理提高盈利能力?
  • P2678 [NOIP 2015 提高组] 跳石头
  • 旋转位置编码的论文阅读
  • UE5 基础应用 —— 08 - 动画蓝图 简单使用
  • unity pcd 二进制版 简单显示文件对象(单色)
  • 面试题:Redis要点总结(复制、哨兵、集群)
  • Leetcode 18 java
  • Redis集群为何采用16384个槽的设计?
  • 《树与二叉树详解:概念、结构及应用》