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

算法【二分答案法】

本文需掌握二分搜索。

二分答案法

1.估计最终答案可能的范围是什么,可以定的粗略,反正二分不了几次。

2.分析问题的答案和给定条件之间的单调性,大部分时候只需要用到自然智慧。

3.建立一个f函数,当答案固定的情况下,判断给定的条件是否达标。

4.在最终答案可能的范围上不断二分搜索,每次用f函数判断,直到二分结束,找到最合适的答案。

核心点:分析单调性、建立f函数

下面通过几个题目加深理解。

题目一

测试链接:https://leetcode.cn/problems/koko-eating-bananas/

分析:可以通过题目得到答案的大致范围,通过对答案范围的二分搜索,将每次搜索的答案进行判定,该答案是否合法。搜索完毕即可得到最优的答案。代码如下。

class Solution {
public:bool f(vector<int>& piles, int h, int k){long long hours = 0;int length = piles.size();for(int i = 0;i < length;++i){hours += ((piles[i] + k - 1) / k);}return hours <= h;}int minEatingSpeed(vector<int>& piles, int h) {long long ans;int left = 1;int right = *(max_element(piles.begin(), piles.end()));int middle;while (left <= right){middle = left + ((right - left) >> 1);if(f(piles, h, middle)){right = middle - 1;ans = middle;}else{left = middle + 1;}}return ans;}
};

其中,left和right分别是答案范围的左边界和右边界。f方法返回以k速度吃香蕉是否能在h小时内吃完。

题目二

测试链接:https://leetcode.cn/problems/split-array-largest-sum/

分析:二分答案法思路都差不多,主要是f函数的意义,范围的设置等,后面的题目不再赘述。代码如下。

class Solution {
public:bool f(vector<int>& nums, int he, int k){int group = 0;int sum = 0;for(int i = 0;i < nums.size();++i){if(nums[i] > he){return false;}if(sum + nums[i] > he){sum = nums[i];++group;}else{sum += nums[i];}}return (group + 1) <= k;}int splitArray(vector<int>& nums, int k) {int ans;int left = 0;int right = accumulate(nums.begin(), nums.end(), 0);int middle;while (left <= right){middle = left + ((right - left) >> 1);if(f(nums, middle, k)){right = middle - 1;ans = middle;}else{left = middle + 1;}}return ans;}
};

其中,f方法返回的是将nums数组分为和不超过he的子数组的个数是否小于等于k。

题目三

测试链接:https://www.nowcoder.com/practice/7037a3d57bbd4336856b8e16a9cafd71

分析:代码如下。

#include <iostream>
#include <vector>
using namespace std;
int N;
int max_value;
vector <int> H;
bool f(int E){for(int i = 0;i < N;++i){E += (E - H[i]);if(E >= max_value){return true;}if(E < 0){return false;}}return true;
}
int main(void){int ans;int left = 0;int right = 0;int middle;scanf("%d", &N);H.assign(N, 0);for(int i = 0;i < N;++i){scanf("%d", &H[i]);right = right > H[i] ? right : H[i];}max_value = right;while (left <= right){middle = left + ((right - left) >> 1);if(f(middle)){right = middle - 1;ans = middle;}else{left = middle + 1;}}printf("%d", ans);
}

其中,f函数返回以E能量值出发能否到终点。

题目四

测试链接:https://leetcode.cn/problems/find-k-th-smallest-pair-distance/

分析:代码如下。

class Solution {
public:bool f(vector<int>& nums, int k, int value, int length){int num = 0;for(int left = 0, right = 1;left < length && right < length;++right){if(nums[right] - nums[left] < value){num += (right - left);}else{while (nums[right] - nums[left] >= value && left < right){++left;}num += (right - left);}}return num >= k;}int smallestDistancePair(vector<int>& nums, int k) {sort(nums.begin(), nums.end());int length = nums.size();int ans;int left = 0;int right = nums[length-1] - nums[0];int middle;while (left <= right){middle = left + ((right - left) >> 1);if(f(nums, k, middle, length)){right = middle - 1;}else{left = middle + 1;ans = middle;}}return ans;}
};

其中,f方法返回在nums数组中比value小的数对的个数是否大于等于k,通过nums数组中比value小的数对的个数采用滑动窗口求解。

题目五

测试链接:https://leetcode.cn/problems/maximum-running-time-of-n-computers/

分析:代码如下。

class Solution {
public:bool f(vector<int>& batteries, int n, long long minutes, int index){int num = 0;while (index - num >= 0 && batteries[index-num] > minutes){++num;}n -= num;index -= num;long long sum = 0;for(int i = 0;i <= index;++i){sum += batteries[i];}return sum >= (long long)n * (long long)minutes;}long long maxRunTime(int n, vector<int>& batteries) {long long ans;long long left = 0;long long right = accumulate(batteries.begin(), batteries.end(), (long long)0);long long middle;int length = batteries.size();sort(batteries.begin(), batteries.end());while (left <= right){middle = left + ((right - left) >> 1);if(f(batteries, n, middle, length-1)){ans = middle;left = middle + 1;}else{right = middle - 1;}}return ans;}
};

其中,f方法返回n台电脑能够同时运行minutes分钟。

采用这种思路,速度较慢,可以加入一个贪心优化一下。代码如下。

class Solution {
public:bool f(vector<int>& batteries, int n, long long minutes, int length){long long sum = 0;for(int i = 0;i < length;++i){if(batteries[i] > minutes){--n;}else{sum += batteries[i];}if(sum >= (long long)n * (long long)minutes){return true;}}return false;}long long maxRunTime(int n, vector<int>& batteries) {long long ans;long long left = 0;long long right;long long middle;long long sum = 0;int max_value = 0;int length = batteries.size();for(int i = 0;i < length;++i){sum += batteries[i];max_value = max_value > batteries[i] ? max_value : batteries[i];}right = sum;if(sum >= (long long)max_value * (long long)n){return sum / n;}while (left <= right){middle = left + ((right - left) >> 1);if(f(batteries, n, middle, length)){ans = middle;left = middle + 1;}else{right = middle - 1;}}return ans;}
};

加入了如果数组的和大于等于数组最大值与电脑台数的乘积,则直接返回数组和除以电脑台数即是答案的判断。其他部分,与之前思路一样,改变一些写法。

题目六

题目描述:计算等位时间。给定一个数组arr长度为n,表示n个服务员,每服务一个客人的时间。给定一个正数m,表示有m个人等位,如果你是刚来的人,每个客人都遵循有空位就上的原则。请问你需要等多久?假设m远远大于n,比如n <= 10^3, m <= 10^9,该怎么做是最优解?

谷歌的面试,这个题连考了2个月。

分析:代码如下。

class Solution {
public:bool f(vector<int>& arr, int m, int minutes, int length){int sum = 0;for(int i = 0;i < length;++i){sum += ((minutes + arr[i] - 1)/ arr[i]);}return sum >= m+1;}int WaitTime(vector<int>& arr, int m) {int ans;int length = arr.size();int left = 0;int right = *(min_element(arr.begin(), arr.end()));int middle;while (left <= right){middle = left + ((right - left) >> 1);if(f(arr, m, middle, length)){right = middle - 1;ans = middle;}else{left = middle + 1;}}return ans;}
};

其中,f方法返回minutes分钟能不能服务到自己。

题目七

题目描述:

刀砍毒杀怪兽问题。怪兽的初始血量是一个整数hp,给出每一回合刀砍和毒杀的数值cuts和poisons。第i回合如果用刀砍,怪兽在这回合会直接损失cuts[i]的血,不再有后续效果。第i回合如果用毒杀,怪兽在这回合不会损失血量,但是之后每回合都损失poisons[i]的血量。并且你选择的所有毒杀效果,在之后的回合会叠加。两个数组cuts、poisons,长度都是n,代表你一共可以进行n回合。每一回合你只能选择刀砍或者毒杀中的一个动作。如果你在n个回合内没有直接杀死怪兽,意味着你已经无法有新的行动了。但是怪兽如果有中毒效果的话,那么怪兽依然会不停扣血,直到血量耗尽的那回合死掉。返回至少多少回合怪兽会死掉。

数据范围 : 1<=n<=10^5;1<=hp<=10^9;1<=cuts[i]、poisons[i]<=10^9

真实大厂算法笔试题。

分析:代码如下。

class Solution {
public:bool f(int hp, vector<int>& cuts, vector<int>& poisons, int round, int length){for(int i = 0;i < length && i < round;++i){if(cuts[i] >= (round - i - 1) * poisons[i]){hp -= cuts[i];}else{hp -= ((round - i - 1) * poisons[i]);}if(hp <= 0){return true;}}return false;}int ShortestRound(int hp, vector<int>& cuts, vector<int>& poisons) {int ans;int length = cuts.size();int left = 1;int right = hp+1;int middle;while (left <= right){middle = left + ((right - left) >> 1);if(f(hp, cuts, poisons, middle, length)){right = middle - 1;ans = middle;}else{left = middle + 1;}}return ans;}
};

其中,f方法返回在round回合能否杀死boss。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 解锁多场景,EasyCVR视频汇聚网关赋能业务数字化转型
  • 系统内存管理:虚拟内存、内存分段与分页、页表缓存TLB以及Linux内存管理
  • Linux 初级面试题目-45题 总结
  • 插值算法在数学建模中的应用:以淡水养殖池塘数据为例
  • Android 手机恢复出厂设置后,还能恢复其中数据吗?
  • 8.3.数据库基础技术-关系代数
  • 算法刷题day33|动态规划:322. 零钱兑换、279. 完全平方数、139. 单词拆分
  • 【MySQL 核心】MySQL数据恢复-dbsake
  • 工厂模式和策略模式的区别以及使用
  • LLM 学习之「向量数据库」
  • FreeSWITCH
  • ZLMediaKit如何结合webrtc实现双向对讲
  • 【MySQL】2.MySQL实际操作
  • [C#数据加密]——MD5、SHA、AES、RSA
  • Chainlit快速实现AI对话应用将聊天数据的持久化到Mongo非关系数据库中
  • python3.6+scrapy+mysql 爬虫实战
  • [nginx文档翻译系列] 控制nginx
  • 【Under-the-hood-ReactJS-Part0】React源码解读
  • 11111111
  • 5、React组件事件详解
  • CSS实用技巧干货
  • Debian下无root权限使用Python访问Oracle
  • Docker 1.12实践:Docker Service、Stack与分布式应用捆绑包
  • Koa2 之文件上传下载
  • Linux编程学习笔记 | Linux IO学习[1] - 文件IO
  • Map集合、散列表、红黑树介绍
  • Mybatis初体验
  • Netty 框架总结「ChannelHandler 及 EventLoop」
  • Promise面试题2实现异步串行执行
  • Python_网络编程
  • vuex 笔记整理
  • windows下mongoDB的环境配置
  • 搭建gitbook 和 访问权限认证
  • 经典排序算法及其 Java 实现
  • 配置 PM2 实现代码自动发布
  • 扫描识别控件Dynamic Web TWAIN v12.2发布,改进SSL证书
  • 使用agvtool更改app version/build
  • 小程序 setData 学问多
  • 用Canvas画一棵二叉树
  • puppet连载22:define用法
  • ​LeetCode解法汇总2304. 网格中的最小路径代价
  • ​草莓熊python turtle绘图代码(玫瑰花版)附源代码
  • ## 1.3.Git命令
  • #git 撤消对文件的更改
  • #include到底该写在哪
  • #微信小程序(布局、渲染层基础知识)
  • (2)关于RabbitMq 的 Topic Exchange 主题交换机
  • (C语言)深入理解指针2之野指针与传值与传址与assert断言
  • (Ruby)Ubuntu12.04安装Rails环境
  • (web自动化测试+python)1
  • (zz)子曾经曰过:先有司,赦小过,举贤才
  • (附源码)spring boot儿童教育管理系统 毕业设计 281442
  • (附源码)SSM环卫人员管理平台 计算机毕设36412
  • (附源码)计算机毕业设计ssm电影分享网站
  • (六)DockerCompose安装与配置