二分查找和二分答案(基础)

article/2025/6/14 15:21:38

目录

前言

二分的本质

二分的代码实现

二分查找

题目

洛谷 P1571 眼红的Medusa

洛谷 P1102 A-B 数对

洛谷 P1678 烦恼的高考志愿

OpenJudge 01:查找最接近的元素

二分答案

实现

题目

洛谷 P1824 进击的奶牛

洛谷 P1182 数列分段 Section ||

洛谷 P1281 书的复制


前言

前面的题比较简单,但看到最后有惊喜哦。

做题时先想暴力解,再优化,算法的目的是优化时间或空间或简化问题。

  • 答案具有单调性,且能在O(n)时间内判定用二分优化。
  • 暴力解是dfs求方案最值等,用动规优化,dfs本身就划分了阶段,参数可作为动规状态设计的参考
  • 单调栈、堆等优化
  • 线段树优化区间查询、求和等

二分的本质

二分是为了优化在一段具有单调性的区间里查找值的时间,时间复杂度O(log n),每次查找确定区间的范围和区间的中间位置,查找的值和中间位置的值作比较,确定是哪个方向,来缩小区间。

能用二分的区间一定具有单调性,这样才能确定是去区间的左半段查找还是右半段查找。

二分查找是通过二分查找值,二分答案是通过二分确定结果(结果具有单调性),两者的思想相同,只是二分的应用不同。

二分的代码实现

1.区间定义(区间[L, R]中包含要查的值):明确区间定义即可明确结束条件,明确区间如何缩小

2.结束条件:考虑即将结束时的区间和结果

3.结果:一般为L或mid

二分查找

二分的运用,一般是查找元素,要求区间必须有序。

题目

洛谷 P1571 眼红的Medusa

P1571 眼红的Medusa - 洛谷

先想暴力再想优化。耗时源是查找,用二分查找优化,先对b数组排序。

代码如下:

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;typedef long long LL;const LL Maxn = 1e5 + 5;
const LL Maxm = 1e5 + 5;LL avct[Maxn], bvct[Maxm];int main() {ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);LL n, m;cin >> n >> m;for (LL i = 1; i <= n; ++i)  cin >> avct[i];for (LL i = 1; i <= m; ++i)  cin >> bvct[i];sort(bvct + 1, bvct + m + 1);LL L = 1, R = n, mid = 0;for (LL i = 1; i <= n; ++i) {L = 1, R = m;while (L <= R) {mid = L + ((R - L) >> 1);if (bvct[mid] < avct[i])  L = mid + 1;else if (bvct[mid] > avct[i])  R = mid - 1;else {cout << bvct[mid] << ' ';break;}}}return 0;
}

mid为结果,所以结束条件为L <= R,在L = R时再获取一次mid得到结果。

可用哈希解。

洛谷 P1102 A-B 数对

P1102 A-B 数对 - 洛谷

将A - B = C转化为A = B + C,B、C已知,二分查找数组中是否有B + C。

不会重复计算,A = B + C,但B ≠ A + C。

可用哈希解。

代码如下:

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;typedef long long LL;const LL Maxn = 2e5 + 5;LL invt[Maxn], vct[Maxn], num[Maxn];LL f_search(LL x, LL n) {LL L = 1, R = n, mid = 0;while (L <= R) {mid = L + ((R - L) >> 1);if (vct[mid] < x)  L = mid + 1;else if (vct[mid] > x)  R = mid - 1;else  return mid;}return -1;
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);LL n, c;cin >> n >> c;for (LL i = 1; i <= n; ++i) cin >> invt[i];sort(invt + 1, invt + n + 1);LL cnt = 0, idx = 0, res = 0;for (LL i = 1; i <= n; ++i) {if (invt[i] != invt[i - 1])  vct[++cnt] = invt[i];++num[cnt];}for (LL i = 1; i <= cnt; ++i) {idx = f_search(vct[i] + c, cnt);if (idx != -1) {res += num[i] * num[idx];}}cout << res;return 0;
}

洛谷 P1678 烦恼的高考志愿

P1678 烦恼的高考志愿 - 洛谷

设估分为x,估分最小相差值为w,则w = min(≤x的最大值,>=x的最小值)。二分查找。

代码如下:

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;typedef long long LL;const LL Maxm = 1e5 + 5;
const LL Maxn = 1e5 + 5;
const LL MVal = 1e14 + 5;LL vctm[Maxm], vctn[Maxn];// <= x的最大值
LL f_search1(LL x, LL m) {LL L = 1, R = m, mid = 0;while (L < R) {mid = L + ((R - L) >> 1) + 1;if (vctm[mid] <= x)  L = mid;else  R = mid - 1;}return vctm[L];
}// >= x的最小值
LL f_search2(LL x, LL m) {LL L = 1, R = m, mid = 0;while (L < R) {mid = L + ((R - L) >> 1);if (vctm[mid] < x)  L = mid + 1;else  R = mid;}return vctm[L];
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);LL m, n;cin >> m >> n;for (LL i = 1; i <= m; ++i)  cin >> vctm[i];for (LL i = 1; i <= n; ++i)  cin >> vctn[i];vctm[0] = -5, vctm[m + 1] = MVal;sort(vctm + 1, vctm + m + 1);LL res = 0;for (LL i = 1; i <= n; ++i) {res += min(abs(vctn[i] - f_search1(vctn[i], m)), abs(vctn[i] - f_search2(vctn[i], m)));}cout << res;return 0;
}

f_search1函数mid = (L + R + 1) / 2 = L + ((R - L) >> 1) + 1,当L + 1 = R  vctm[mid] <= x时,若mid = (L + R) / 2 = L,L赋值为L,会死循环。

f_search2函数mid = (L + R) / 2 = L + ((R - L) >> 1),当L + 1 = R  vctm[mid] <= x时,若mid = (L + R) / 2 + 1 = R,L赋值为R + 1,区间外不是结果。

OpenJudge 01:查找最接近的元素

OpenJudge - 01:查找最接近的元素

代码如下:

#include <iostream>
#include <cmath>
using namespace std;typedef long long LL;const LL Maxn = 1e5 + 5;
const LL MVal = 1e14;LL vct[Maxn];// <= x 的最大值
LL f_search1(LL x, LL n) {LL L = 1, R = n, mid = 0;while (L < R) {mid = L + ((R - L) >> 1) + 1;if (vct[mid] > x)  R = mid - 1;else  L = mid;}return vct[L];
}// >= x的最小值
LL f_search2(LL x, LL n) {LL L = 1, R = n, mid = 0;while (L < R) {mid = L + ((R - L) >> 1);if (vct[mid] < x)  L = mid + 1;else  R = mid;}return vct[L];
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);LL n, m, num, res1, res2;cin >> n;for (LL i = 1; i <= n; ++i)  cin >> vct[i];vct[0] = -5, vct[n + 1] = MVal;cin >> m;while (m--) {cin >> num;res1 = f_search1(num, n);res2 = f_search2(num, n);if (abs(num - res1) > abs(num - res2))  cout << res2 << '\n';else  cout << res1 << '\n';}return 0;
}

二分答案

二分的运用,最优解问题可抽象为函数,函数的“定义域”为方案,“值域”为最优解(结果)。设S为结果,结果越大越优,对于任意x > S的x不是结果,否则和S为最优解条件冲突,任意x < s不是最优解,S位于一个分界线上,具有单调性,可用二分+判定得到结果。

判定函数必须能在O(n)时间内判定,否则用二分的效率约等于暴力。

实现

1.最优解:明确如何二分

2.写出二分:整体框架

3.判定函数:判定给的值是否可行,确定区间如何缩小。一般用贪心实现,最贪了可行,说明可行,最贪了不可行,说明无论如何不可行。

题目

洛谷 P1824 进击的奶牛

P1824 进击的奶牛 - 洛谷

最优解:最大的最近距离,满足单调性

二分:if的区间调整是L = mid,mid可能是结果。

判定函数:设给定值为x,贪心,每个牛的间隔是>=x的最小值(最优策略),统计可放下几头牛,return 放的牛数 >= C,需用pre记录前一个牛所在的隔间坐标,隔间坐标要排序。

代码如下:

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;typedef long long LL;const LL Maxn = 1e5 + 5;LL vct[Maxn];bool f_check(LL num, LL n, LL c) {LL pre = vct[1], res = 1;for (LL i = 2; i <= n; ++i) {if (vct[i] - pre >= num) {++res;pre = vct[i];}}return res >= c;
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);LL n, c;cin >> n >> c;for (LL i = 1; i <= n; ++i)  cin >> vct[i];sort(vct + 1, vct + n + 1);LL L = 1, R = vct[n] - vct[1], mid = 0;while (L < R) {mid = L + ((R - L) >> 1) + 1;if (f_check(mid, n, c) != false)  L = mid;else  R = mid - 1;}cout << L << '\n';return 0;
}

洛谷 P1182 数列分段 Section ||

P1182 数列分段 Section II - 洛谷

最优解:每段和最大值最小

判定函数:贪心,设给定的参数(每段和最大值)为x,vct为前缀和,sum为这段之前的段的和。若vct[i] - sum > x,将i分配到下一段,++cnt,统计可尽可能少的分为几段,设分的段数为cnt,若cnt < m,说明若分成m段,每段和最大值 <= x,若cnt = m,说明分成m段,每段和最大值 = x。

举一反三:若段不是连续的,就……

#include <iostream>
#include <cmath>
using namespace std;typedef long long LL;const LL Maxn = 1e5 + 5;LL vct[Maxn];bool f_check(LL x, LL n, LL m) {LL cnt = 1, sum = 0;for (LL i = 1; i <= n; ++i) {if (vct[i] - sum > x) {sum = vct[i - 1];++cnt;}}return cnt <= m;
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);LL n, m, mVal = 0;cin >> n >> m;for (LL i = 1; i <= n; ++i) {cin >> vct[i];mVal = max(mVal, vct[i]);vct[i] += vct[i - 1];}LL L = mVal, R = vct[n], mid = 0;while (L < R) {mid = L + ((R - L) >> 1);if (f_check(mid, n, m) != false)  R = mid;else  L = mid + 1;}cout << L << '\n';return 0;
}

洛谷 P1281 书的复制

P1281 书的复制 - 洛谷

最优解:每个人抄写的书是连续的且每个人至少抄一本书,抄写页数最多的人的抄写页数,可用二分

判定函数:类似上一题,但倒着遍历,因为要求前面的人少写。

求结果:要获取具体的方案,L为最少的抄写页数最多的人的抄写页数,倒着遍历,idx记录当前人的终止编号,初始为m,若vct[i] - sum > L,则说明前面为一个人的抄写,push_back进result,idx = i,最后还要push_back一下,倒着输出result即为答案,每个人都有活可干,所以result.size() 等于k。

#include <iostream>
#include <vector>
#include <cmath>
using namespace std;typedef long long LL;const LL Maxm = 500 + 5;LL vct[Maxm];
vector<pair<LL, LL> > result;void getRes(LL x, LL m, LL k) {LL cnt = 1, sum = 0, idx = m;result.clear();for (LL i = m; i >= 1; --i) {if (vct[i] - sum > x) {sum = vct[i + 1];++cnt;result.push_back({i + 1, idx});idx = i;}}result.push_back({1, idx});
}bool f_check(LL x, LL m, LL k) {LL cnt = 1, sum = 0;for (LL i = m; i >= 1; --i) {if (vct[i] - sum > x) {sum = vct[i + 1];++cnt;}}return cnt <= k;
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);LL m, k, mVal = 0;cin >> m >> k;for (LL i = 1; i <= m; ++i) {cin >> vct[i];mVal = max(vct[i], mVal);}for (LL i = m - 1; i >= 1; --i)  vct[i] += vct[i + 1];LL L = mVal, R = vct[1], mid = 0;while (L < R) {mid = L + ((R - L) >> 1);if (f_check(mid, m, k) != false)  R = mid;else  L = mid + 1;}getRes(L, m, k);for (LL i = result.size() - 1; i >= 0; --i)  cout << result[i].first << ' ' << result[i].second << '\n';return 0;
}

测试点情况如下:

可用动规解,但二分时间复杂度最优。


http://www.hkcw.cn/article/DRCpDDMYbj.shtml

相关文章

三极管和MOS的三种状态命名的区别

前言 还记得大学用MOS做仿真&#xff0c;来进行原理说明时&#xff0c;总是会将三极管和MOS的叫法搞混。本篇文章就重新回顾&#xff0c;加深下印象。 1. 三极管&#xff08;BJT&#xff09;的三个工作状态 BJT 是电流控制型器件&#xff0c;其工作状态由 基极电流 IB​ 和 集…

SKUA-GOCAD入门教程-第八节 线的创建与编辑2

8.1.3根据线创建曲线 (1)从线生成线 这个命令可以将一组曲线合并为一条曲线。每个输入曲线都会成为新曲线内的一个部分。 1、选择 Curve commands > New > Curves 打开对话框。 图1 根据曲线创建曲线 在“name”框中:输入新建线的名称。在“Curves”框中:输入用于…

关于easyx头文件

一、窗口创建 &#xff08;1&#xff09;几种创建方式 #include<easyx.h>//easyx的头文件 #include<iostream> using namespace std;int main() {//创建一个500*500的窗口//参数为&#xff1a;长度&#xff0c;宽度&#xff0c;是否显示黑框&#xff08;无参为不…

基于VLC的Unity视频播放器(四)

上篇文章中提到的问题 播放某个m3u8地址时会嘎掉&#xff0c;想办法解决了一下&#xff0c;很粗暴的&#xff0c;先SetFormat&#xff0c;再Stop&#xff0c;最后再Play&#xff0c;能用…… if (player ! null && player.GetSize() 0) {player.GetSize((w, h) >…

邢台山峰特种橡胶制品有限公司专题报道

在河北任泽经济开发区的现代化厂房里&#xff0c;全自动硫化机正以0.01毫米的精度压制着油封。这里生产的特种橡胶制品&#xff0c;已悄然进入全球90多个国家的工业供应链。作为邢台市橡塑新材料产业集群的企业&#xff0c;邢台山峰特种橡胶制品有限公司用25项专利技术&#xf…

单文件制作工具 7.0.2.3856

【​本章下载一】&#xff1a;https://pan.xunlei.com/s/VORkoGbMcUDScW2C5kyqJla8A1?pwdegvq# 【​本章下载二】&#xff1a;https://pan.xunlei.com/s/VORkoGbMcUDScW2C5kyqJla8A1?pwdegvq# 【百款黑科技】&#xff1a;https://ucnygalh6wle.feishu.cn/wiki/HPQywvPc7iLZu1…

打破 GIS 数据处理瓶颈!GISBox 的九种切片方式

在地理信息系统&#xff08;GIS&#xff09;领域&#xff0c;数据格式的多样性和复杂性一直是制约高效处理与应用的瓶颈。从倾斜摄影模型到BIM设计图纸&#xff0c;从地形影像到点云数据&#xff0c;每一种数据类型都需要精准且高效的切片处理&#xff0c;以实现流畅的三维可视…

Matlab回归预测大合集又更新啦!新增2种高斯过程回归预测模型,已更新41个模型!性价比拉满!

Matlab回归预测大合集又更新啦&#xff01;新增2种高斯过程回归预测模型&#xff0c;已更新41个模型&#xff01;性价比拉满&#xff01; 目录 Matlab回归预测大合集又更新啦&#xff01;新增2种高斯过程回归预测模型&#xff0c;已更新41个模型&#xff01;性价比拉满&#xf…

中英混合编码解码全解析

qwen模型分词器怎么映射的:中英混合编码解码全解析 中英文混合编码与解码的过程,本质是 字符编码标准(如 UTF-8)对多语言字符的统一处理 ,核心逻辑围绕“字节序列 ↔ 字符映射”展开 北京智源人工智能研究院中文tokenID qwen模型分词器文件 一、编码阶段:统一转为字节序…

【散刷】二叉树基础OJ题(二)

&#x1f4dd;前言说明&#xff1a; 本专栏主要记录本人的基础算法学习以及刷题记录&#xff0c;使用语言为C。 每道题我会给出LeetCode上的题号&#xff08;如果有题号&#xff09;&#xff0c;题目&#xff0c;以及最后通过的代码。没有题号的题目大多来自牛客网。对于题目的…

深度学习与特征交叉:揭秘FNN与SNN在点击率预测中的应用

今天&#xff0c;给大家分享一篇关于基于深度神经网络&#xff08;DNNs&#xff09;的特征交叉方法——FNN&#xff08;Factorization-machine supported Neural Network&#xff09;和SNN&#xff08;Sampling-based Neural Network&#xff09;的研究。随着广告点击率预估等领…

Win11系统不推送24H2/西数SSD无法安装24H2 - 解决方案

Win11系统不推送24H2/西数SSD无法安装24H2 - 解决方案 前言获取24H2推送西数SSD安装24H2更新SSD固件规避设备检查修改注册表&#xff08;可选&#xff09; 前言 Win11 24H2系统优化了底层架构&#xff0c;加快了系统响应速度&#xff0c;并在25年5月份开始推送&#xff0c;但很…

Elasticsearch集群最大分片数设置详解:从问题到解决方案

目录 前言 1 问题背景&#xff1a;重启后设置失效 2 核心概念解析 2.1 什么是分片(Shard)&#xff1f; 2.2 cluster.max_shards_per_node的作用 2.3 默认值是多少&#xff1f; 3 参数设置的两种方式 3.2 持久性设置(persistent) 3.2 临时设置(transient) 4 问题解决方…

机器学习:集成学习概念、分类、随机森林

本文目录&#xff1a; 一、集成学习概念**核心思想&#xff1a;** 二、集成学习分类&#xff08;一&#xff09;Bagging集成&#xff08;二&#xff09;Boosting集成(三&#xff09;两种集成方法对比 三、随机森林 一、集成学习概念 集成学习是一种通过结合多个基学习器&#…

Python基于SVM技术的手写数字识别问题项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在当今数字化转型加速的时代&#xff0c;手写数字识别作为图像处理与机器学习领域的一个经典问题&#xff0c;具有广…

MySQL 全量、增量备份与恢复

一.MySQL 数据库备份概述 备份的主要目的是灾难恢复&#xff0c;备份还可以测试应用、回滚数据修改、查询历史数据、审计等。之前已经学习过如何安装 MySQL&#xff0c;本小节将从生产运维的角度了解备份恢复的分类与方法。 1 数据备份的重要性 在企业中数据的价值至关…

Python Pytest

1.Pytest用例发现规则 1.1 模块名(python文件)名必须以 test_ 开头或 _test 结尾&#xff0c;如 test_case&#xff0c;case_test&#xff0c;下划线都不能少 1.2 模块不能放在 . 开头的隐藏目录或者叫 venv的目录下&#xff0c;virtual environment&#xff0c;叫venv1都可以…

[Linux] MySQL源码编译安装

目录 环境包安装 创建程序用户 解压源码包 配置cmake ​编辑编译 安装 配置修改属性 属主和属组替换成mysql用户管理 系统环境变量配置 初始化数据库 服务管理 启动 环境包安装 yum -y install ncurses ncurses-devel bison cmake gcc gcc-c 重点强调&#xff1a;采…

RK3568-移植codesys-runtime

PC下载安装CODESYS Development System V3.5.17.0 https://store.codesys.com/en/codesys.html#product.attributes.wrapperPC下载安装 CODESYS Control for Linux ARM64 SL 4.1.0.0.package https://store.codesys.com/en/codesys-control-for-linux-arm-sl-1.html 注意&…

安装和配置 Nginx 和 Mysql —— 一步一步配置 Ubuntu Server 的 NodeJS 服务器详细实录6

前言 昨天更新了四篇博客&#xff0c;我们顺利的 安装了 ubuntu server 服务器&#xff0c;并且配置好了 ssh 免密登录服务器&#xff0c;安装好了 服务器常用软件安装, 配置好了 zsh 和 vim 以及 通过 NVM 安装好Nodejs&#xff0c;还有PNPM包管理工具 。 作为服务器的运行…