数据结构与算法:二分答案法

前言

二分答案法是很牛逼的一种算法,本质思想就是猜答案,然后看能不能对上条件。

一、内容

1.使用条件

只有当让你输出的答案只有一个数的时候,且答案与给定条件之间存在单调性关系时才能使用。

2.步骤

首先,要先确定答案那个数的范围。(范围大点无所谓)

之后,去找答案与给定条件的单调性关系。

然后使用二分搜索去范围上搜答案,每次使用一个函数判断答案能否满足题意。

二、题目

1.爱吃香蕉的珂珂

class Solution {
public:int minEatingSpeed(vector<int>& piles, int h) {int l=1;int r=0;for(int i=0;i<piles.size();i++){r=max(r,piles[i]);}int ans=0;int m;while(l<=r){m=(l+r)/2;if(f(m,piles)<=h){ans=m;r=m-1;}else {l=m+1;}}return ans;}long long f(int speed,vector<int>&piles){long long ans=0;for(int i=0;i<piles.size();i++){ans+=(piles[i]+speed-1)/speed;//向上取整}return ans;}
};

 这个题很明显能想出,要求返回的速度与时间之间存在单调性关系,速度越大,时间越短。而且,这个速度存在一个范围,即1~每堆香蕉的数量最大值。因为速度最慢就是一次只吃一个,最快就是一次能把一堆都吃了,再快也没意义。

之后就是在答案范围上二分搜索,当此时的速度能满足时间要求,就记一下答案,然后去左侧看看速度更小时能否也满足时间;否则不记答案去右侧二分。

再就是判断函数,根据题意,让它按顺序吃就行,又因为一次最多只能吃一堆,所以这里相除时要向上取整,保证最后多吃一次把余数吃完。

2.分割数组的最大值

class Solution {
public:typedef long long ll;int splitArray(vector<int>& nums, int k) {ll l=0;ll r=0;for(int i=0;i<nums.size();i++){r+=nums[i];}ll ans=0;ll m;while(l<=r){m=(l+r)/2;if(f(m,nums)<=k){ans=m;r=m-1;}else {l=m+1;}}return (int)ans;}ll f(int target,vector<int>&nums){ll ans=0;for(int i=0,sum=0;i<nums.size();i++){if(nums[i]>target){return INT_MAX;}if(sum+nums[i]>target){ans++;sum=nums[i];}else{sum+=nums[i];}}return ans+1;}
};

这个题相比上一个题就不是那么好想了,首先分析单调性,可以发现,分割数和要求返回的最大值存在单调性关系,分割数越大,最大值越小。

之后是这个累加和最大值的范围,可以想到,最小就是0,最大就是不分割时整体的累加和。

之后就去二分搜索,当此时的累加和能分出的子数组数量比要求的小时,就记答案,然后去左侧更小的累加和看看能不能也满足要求的分割数;否则就不记答案去右侧二分。

判断函数就是若有一个数本身就比目标累加和大,直接返回无穷大表示不可能这么分;否则,若累加和比目标大,就让分割数+1即可。

3.机器人跳跃问题

#include<bits/stdc++.h>
using namespace std;bool f(int energy,int MAX,vector<int>&height)
{for(int i=1;i<height.size();i++){if(energy<=height[i]){energy-=height[i]-energy;}else{energy+=energy-height[i];}if(energy>=MAX){return true;}if(energy<0){return false;}}return true;
}int findEnergy(vector<int>&height)
{int MAX=0;for(int i=1;i<height.size();i++){MAX=max(MAX,height[i]);}int ans=0;int l=0;int r=MAX;int m;while(l<=r){m=(l+r)/2;if(f(m,MAX,height)){ans=m;r=m-1;}else{l=m+1;}}return ans;
}void solve()
{int n;cin>>n;vector<int>height(n+1,0);for(int i=1;i<=n;i++){cin>>height[i];}cout<<findEnergy(height);
}int main()
{solve();return 0;
}

这个题的单调性能比上个题好想不少,分析可知,初始能量越大,越有可能到达终点。

之后是初始能量的范围,最小就是从0开始,最大就是高度的最大值,这样肯定能完成。

然后去二分,当此时能量能完成时,就记一下答案,然后去左侧看看更小的能量能否完成;否则不记答案去右侧二分。

注意判断函数里,为了剪枝和防止溢出,当此时能量比高度最大值大了就直接返回true,当能量小于0了就直接返回false。

4.找出第 K 小的数对距离

class Solution {
public:int smallestDistancePair(vector<int>& nums, int k) {//先排序sort(nums.begin(),nums.end());int l=0;int r=nums[nums.size()-1]-nums[0];int m;int ans=0;while(l<=r){m=(l+r)/2;if(f(m,nums)>=k){ans=m;r=m-1;}else{l=m+1;}}return ans;}//差值<=limit的数对有几对int f(int limit,vector<int>&nums){int ans=0;for(int l=0,r=0;l<nums.size();l++){while(r+1<nums.size()&&nums[r+1]-nums[l]<=limit){r++;}//l~r范围上的数差值均<=limitans+=r-l;}return ans;}
};

这个题和第二题的单调性说实话是真不好想,这个题是数对距离和数对数量存在单调性关系,距离越小,所对应的数对数量越多。

之后是范围,距离最小值就是0,两个数相等,最大就是数字最大值-最小值。

二分时,当此时的数对数量比要求的大时,就记答案,然后去左侧更小的地方看看能否也满足。

判断时,为了方便统计数对数量,所以先对数组排序。因为排序后子数组范围越大,距离越大,所以考虑使用滑动窗口。当距离小于等于limit时就让窗口增大,否则统计答案,即r-l的窗口大小,然后缩小窗口。

太难想了……

5.同时运行 N 台电脑的最长时间

class Solution {
public:long long maxRunTime(int n, vector<int>& batteries) {int MAX=0;long long sum=0;for(int i=0;i<batteries.size();i++){MAX=max(MAX,batteries[i]);sum+=batteries[i];}//若sum>=max*n,说明全是碎片电池if(sum>=(long long)MAX*n){return sum/n;}int ans=0;int l=0;int r=MAX;int m;while(l<=r){m=(l+r)/2;if(f(m,n,batteries)){ans=m;l=m+1;}else{r=m-1;}}return ans;}bool f(int time,int n,vector<int>&batteries){for(long long i=0,sum=0;i<batteries.size();i++){if(batteries[i]>time){n--;}else{sum+=batteries[i];}if(sum>=(long long)time*n){return true;}}return false;}
};

这个比上个题能好想不少,单调性就是时间越长,越难达成。

其次是时间的范围。最小值肯定是0,最大值最初想到的肯定是所有电池的累加和。但如果进一步思考,用一点贪心的思想,当累加和>=所有电池的最大值*电脑数时,说明所有电池都是碎片电池,即不能单独完成要求时间内的供电,所以直接返回sum/n即可。否则,说明存在电池可以单独完成供电,那么此时的时间最大值就变成了电池的最大值。

二分时,当此时的时间能完成时,就记答案,然后去右侧更大的时间看看能否完成。

判断函数就是当有电池能独立完成供电,就让它一直待在这一个电脑,所以n-1;否则就计算电池累加和,若累加和>=时间*电脑数,说明能完成就返回true。

总结

二分答案法真的很妙!可以将复杂的问题转化成很简单的问题,而且由于二分搜索,所以时间复杂度也很优秀。当遇到答案为一个单一的数时就可以分析题目单调性,考虑使用二分答案。所以加上之前的滑动窗口双指针,题目的单调性真的很重要!

END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/71499.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu20.04双系统安装及软件安装(十一):向日葵远程软件

Ubuntu20.04双系统安装及软件安装&#xff08;十一&#xff09;&#xff1a;向日葵远程软件 打开向日葵远程官网&#xff0c;下载图形版本&#xff1a; 在下载目录下打开终端&#xff0c;执行&#xff1a; sudo dpkg -i SunloginClient(按tab键自动补全)出现报错&#xff1a; …

快速生成viso流程图图片形式

我们在写详细设计文档的过程中总会不可避免的涉及到时序图或者流程图的绘制&#xff0c;viso这个软件大部分技术人员都会使用&#xff0c;但是想要画的好看&#xff0c;画的科学还是比较难的&#xff0c;现在我总结一套比较好的方法可以生成好看科学的viso图(图片格式)。主要思…

C++设计一:日期类Date实现

一、引言与概述 1 引言 日期操作是软件开发中的常见需求&#xff0c;如日程管理、数据统计等场景均需处理日期的比较、偏移及合法性校验。为简化此类操作&#xff0c;本文设计了一个高效且类型安全的C日期类Date。 该类通过构造函数内嵌合法性检查&#xff0c;确保对象初始状…

【网络安全】——协议逆向与频繁序列提取:从流量中解码未知协议

目录 引言 一、为什么要结合频繁序列提取&#xff1f; 二、四步融合分析法 步骤1&#xff1a;原始流量采集与预处理 步骤2&#xff1a;多粒度序列模式挖掘 层1&#xff1a;单包内字节级频繁项 层2&#xff1a;跨数据包的行为序列 步骤3&#xff1a;关键字段定位与结构假…

【PAT (Basic Level) Practice】——【数论】1013 数素数

文章目录 一【题目难度】二【题目编号】三【题目描述】四【题目示例】五【解题思路】六【最终得分】七【代码实现】八【提交结果】 一【题目难度】 乙级 二【题目编号】 1013 数素数 三【题目描述】 令 Pi 表示第 i 个素数。现任给两个正整数 M≤N≤104&#xff0c;请输出…

HCIA—IP路由静态

一、概念及作用 1、概念&#xff1a;IP路由是指在IP网络中&#xff0c;数据从源节点到目的节点所经过的路径选择和数据转发的过程。 2、作用 ①实现网络互联&#xff1a;使不同网段的设备能够相互通信&#xff0c;构建大规模的互联网络 ②优化网络拓扑&#xff1a;根据网络…

雷池WAF的为什么选择基于Docker

Docker 是一种开源的容器化平台&#xff0c;可以帮助开发人员将应用程序及其所有依赖项打包到一个称为容器的独立、可移植的环境中。Docker 的核心概念包括以下几点&#xff1a; 容器&#xff1a;Docker 使用容器来封装应用程序及其依赖项&#xff0c;使其能够在任何环境中都能…

图像分类项目2:鸟类图像分类

1 数据集处理 1.1数据集下载 数据集来源&#xff1a;kaggle&#xff0c;网址&#xff1a;https://www.kaggle.com/&#xff0c;点击进入网站&#xff0c;左侧选择Datasets。 进入后搜索栏搜索关键词bird。此时出现很多数据集可以选择&#xff0c;推荐选择第一个或者第三个。…

01_NLP基础之文本处理的基本方法

自然语言处理入门 自然语言处理&#xff08;Natural Language Processing, 简称NLP&#xff09;是计算机科学与语言学中关注于计算机与人类语言间转换的领域&#xff0c;主要目标是让机器能够理解和生成自然语言&#xff0c;这样人们可以通过语言与计算机进行更自然的互动。 …

利用opencv_python(pdf2image、poppler)将pdf每页转为图片

1、安装依赖pdf2image pip install pdf2image 运行.py报错&#xff0c;因为缺少了poppler支持。 2、安装pdf2image的依赖poppler 以上命令直接报错。 改为手工下载&#xff1a; github: Releases oschwartz10612/poppler-windows GitHub 百度网盘&#xff1a; 百度网盘…

IDEA入门及常用快捷键

IDEA是java常用的IDE。当run一个.java文件时&#xff0c;其实是经历了先编译为.class&#xff0c;再运行的过程。 在project文件夹中&#xff0c;out文件夹存储编译的.class文件&#xff0c;src文件夹存储.java代码文件。 设置自动导包 快捷键&#xff1a; 格式化快捷键&…

io学习----->文件io

思维导图&#xff1a; 一.文件io的概念 文件IO&#xff1a;指程序和文件系统之间的数据交互 特点&#xff1a; 1.不存在缓冲区&#xff0c;访问速度慢 2.不可以移植&#xff0c;依赖于操作系统 3.可以访问不同的文件类型(软连接&#xff0c;块设备等) 4.文件IO属于系统调…

深入探索WebGL:解锁网页3D图形的无限可能

深入探索WebGL&#xff1a;解锁网页3D图形的无限可能 引言 。WebGL&#xff0c;作为这一变革中的重要技术&#xff0c;正以其强大的功能和广泛的应用前景&#xff0c;吸引着越来越多的开发者和设计师的关注。本文将深入剖析WebGL的核心原理、关键技术、实践应用&#xff0c;并…

从开发和对抗的角度思考web网页中的接口逆向

如何从开发和对抗的角度去思考web网页中的接口逆向。 文章目录 前言1.从开发和对抗的角度思考接口逆向1.1 什么是接口逆向1.2 开发的角度思考如何开发策略1.3 对抗的角度思考遇到的问题1.4 正常情况下开发者如何防护1.5 正常情况攻击者如何做&#xff1f;1.6 对抗中的胜者 2.某…

C++24--右值引用C++11新特性

目录 1.C11简介 2.统一的列表初始化 2.1{}初始化 2.2std::initializer_list 3.声明 3.1auto 3.2decltype 3.3nullptr 4.范围for循环 5.智能指针 6.右值引用和移动语义 6.1左值引用和右值引用 6.2左值引用与右值引用比较 6.3右值引用使用场景和意义 6.4右值引用引…

Android ChatOn-v1.66.536-598-[构建于ChatGPT和GPT-4o之上]

ChatOn 链接&#xff1a;https://pan.xunlei.com/s/VOKYnq-i3C83CK-HJ1gfLf4gA1?pwdwzwc# 添加了最大无限积分 删除了所有调试信息 语言&#xff1a;全语言支持

Java高频面试之集合-03

hello啊&#xff0c;各位观众姥爷们&#xff01;&#xff01;&#xff01;本baby今天来报道了&#xff01;哈哈哈哈哈嗝&#x1f436; 面试官&#xff1a;说说ArrayList和LinkedList的区别 ArrayList 与 LinkedList 的详细对比 一、底层数据结构 特性ArrayListLinkedList存…

华为hcie证书有什么作用?

新盟教育 专注华为认证培训十余年 为你提供认证一线资讯&#xff01; 在当今数字化飞速发展的时代&#xff0c;ICT行业对专业人才的需求日益增长。华为HCIE证书作为华为认证体系中的最高级别认证&#xff0c;无疑是众多IT从业者追求的目标。那么&#xff0c;华为HCIE证书到底有…

通过微步API接口对单个IP进行查询

import requests import json# 微步API的URL和你的API密钥 API_URL "https://api.threatbook.cn/v3/ip/query" API_KEY "***" # 替换为你的微步API密钥 def query_threatbook(ip):"""查询微步API接口&#xff0c;判断IP是否为可疑"…

Redis|集群 Cluster

文章目录 是什么能干嘛集群算法-分片-槽位slotredis集群的槽位slotredis集群的分片分片槽位的优势slot槽位映射——业界的3种解决方案小厂&#xff1a;哈希取余分区中厂&#xff1a;一致性哈希算法分区大厂&#xff1a;哈希槽分区 面试题&#xff1a;为什么 Redis 集群的最大槽…