概念解释

概述

        二分法在算法竞赛中一般有这么一个用途:在一个具有单调性的解空间中找到符合题意的一个可行解。下面解释几个专有名词:

解空间

        很简单,就是可能存在解的逻辑区域。这个在算法入门时应提到。

可行解

        符合题意的解

单调性

        与数学中的基本是一回事。

算法分析

原理

        就是数学中的逼近原理。通过计算机的高效计算,可以逐步靠近可行解;又由于解空间具有单调性,所以可以简化枚举的过程。简单来讲,二分是一种更高效的枚举手段。

应用

        我们主要是研究在整数域上的二分。

1.二分查找

        在一个序列中查找某一个指定的元素,这可以通过二分查找来实现。下面给出一个例子:

        给出一个整数序列 A ,这个序列有n 个元素。下有m 次询问,每次会询问一个整数,请回答该数是否在 A 中。如果否,请分别输出大于或等于该数的第一个后继,小于或等于该数的第一个前驱以及-1;如果这个数存在,输出以上除-1之外并输出这个数。

        这很显然如果朴素去求,时间复杂度会是O(mn) 的,在1e5及以上的规模下会TLE。下面考虑二分求解。

        回忆概念,我们强调,要在一个单调的解空间中寻找答案。所以考虑对这个序列排序;时间复杂度为O(nlogn)  。 然后执行以下过程:

  • 将区间一分为二;
  • 维护区间的中间端点 mid 。记录 a[mid] 的值。
  • 如果a[mid]\leq x ,收缩左端点,重复以上步骤;
  • 如果a[mid]\geq x ,收缩右端点,重复以上步骤;
  • 直到区间被收缩至只有一个元素,检查这个元素是否是所求。

        过程很简单,时间复杂度也很低,为 O(log_{2}n) 。但是这里有很多的细节需要注意,笔者尽量把这些细节全部展示清楚。首先可以先看下面的参考程序,这里选择结合代码研究。

        取等问题

        很简单,总结出来就一句话:如果A中的元素跟 x 有可能取等,那么收缩区间时,区间端点与 mid 就取等;反之,不能取等(这时往往+1或者-1)。也就是相邻,一边是实心端点,一边是空心端点。还可以这么看:如果取等,意味着 mid 处有可能会是答案,所以收缩区间时,区间端点可以等于 mid 。

        划分问题

        也就是考虑 mid 的取值。通常有两种:

        mid=(l+r)>>1 

        以及

        mid=(l+r+1)>>1

        区别是:

        第一种的写法不会取到 r ,第二种写法不会取到 l 。进一步地,我们知道,第一种是向下取整,称为左中位数;第二种向上取整,则是右中位数。我们可以利用这一性质处理无解的问题:设置最初区间分别为: [0,n] 和 [1,n+1] 把一个越界的下标包括起来,如果最后停止在这个下标上,说明无解。当然,第一种适用于找后继,第二种适用于找前驱。

        STL 函数

        下面介绍两个 STL 的函数,分别是 lower_bound(),upper_bound()。这两个函数分别返回的是在一个单调递增序列中第一个大于或等于某个数的后继,以及第一个大于某数的后继。同样在下面给出手写代码。这里的关键就是上面说的划分问题。

2.二分答案

        这里主要是两类问题,最大值最小化以及最小值最大化。也就是判定问题。我们一开始就说过,

   “ 通过计算机的高效计算,可以逐步靠近可行解;又由于解空间具有单调性,所以可以简化枚举的过程。简单来讲,二分是一种更高效的枚举手段。”

        所以归根结底还是二分查找。请记住:最优化问题往往等价于一个可行性问题的判定。进一步地,是在一个具有严格约束的情况下利用二分法枚举出一个答案来。

        这句话非常有信息量。我们会给出几个等价的表述以及几个例子深刻去理解。

        有这么几种表述:

        建立一个定义域为解空间,值域为0或1的单调分段函数,在这个函数上二分查找分界点。

        其实本质都是一回事。

        下面给出几个例子理解一下。

        最小值最大化

        例题:luogu.P2678 [NOIP 2015 提高组] 跳石头

        二分所求。找出约束:

“由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能移走起点和终点的岩石)。”

        也就是说,M 是约束。这样就容易设计check 函数:每次枚举一个最小值,检查是否合法。由于这个最小值是具有单调性的,所以可以考虑二分出这个最大值。 

        最大值最小化

        例题:luogu.P3853 [TJOI2007] 路标设置

        二分所求。找出约束:

“以及最多可增设的路标数量”

        也就是说,路标数量是约束。这样就容易设计 check 函数:每次枚举一个最大值,检查是否合法。由于这个最大值是具有单调性的,所以可以考虑二分出这个最小值。 

参考程序

在单调递增序列中查找 \geq x 的最小的元素

//
while(l<r)
{int mid=(l+r)>>1;if(a[mid]<=x) l=mid;else r=mid-1;
} 
return a[l];//a[l]==a[r]为终止条件

在单调递增序列中查找 \leq x 的最小的元素

//
while(l<r)
{int mid=(l+r+1)>>1;if(a[mid]>=x) r=mid;else l=mid-1;
} 
return a[l];//a[l]==a[r]为终止条件

最小值最大化

//
#include<iostream>
using namespace std;
int d[50005];
int l,n,m;
bool check(int k)//我们要二分的是最短跳跃距离
{int last=0,ans=0;for(int i=1;i<=n+1;i++){if(d[last]+k>d[i]) ans++;else last=i;}return ans<=m;//是否满足约束
}
int main()
{cin>>l>>n>>m;for(int i=1;i<=n;i++)cin>>d[i];d[n+1]=l;int le=0,ri=l*2;while(le<ri)//二分{int mid=(le+ri+1)>>1;if(check(mid)) le=mid;else ri=mid-1;}cout<<le;return 0;
}

最大值最小化

//
#include<cstdio>
using namespace std;
int d[100005];
int l,n,k;
bool check(int w)//现在就假设我这里的w就是最大值 
{int last=0,ans=0;for(int i=2;i<=n;i++){//while(d[i]>last+w) last+=w,ans++;//我们二分已经是最大距离,如果有比这还大的,那就是要增设 //last=d[i];ans+=(d[i]-d[i-1]-1)/w;}return ans<=k;
}
int main()
{ scanf("%d%d%d",&l,&n,&k);for(int i=1;i<=n;i++)scanf("%d",&d[i]);int le=1,ri=l;while(le<ri){int mid=(le+ri)>>1;if(check(mid)) ri=mid;else le=mid+1;}printf("%d",le);return 0;
}

细节实现

        我们来总结一下二分答案的一般步骤:

  • 判断是否解空间具有单调性——这保证了二分答案的正确性;
  • 找出约束条件——这保证了二分的定义域不是无穷的,也就是保证了最值,即解的存在;
  • 设计check函数,检验枚举过程中的解是否合法;
  • 利用二分查找完成枚举,得出答案。

        由此看来,最优化问题在笔者看来不是“构造”出来的,而是真真切切枚举出来的。在枚举时,保证枚举的元素是题目所要求的最大或最小(假设一定是最大/最小值),也就是把被枚举对象当成一个常量,判断是否符合约束。所以最优化是枚举出来的,最大/最小值是约束出来的。

总结归纳

        二分法的初步应用。日后会有多次更新。

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

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

相关文章

硬核电子工程:从硅片到系统的全栈实战指南—— 融合电路理论、嵌入式开发与PCB设计的工程艺术

一、电路基础&#xff1a;硬件设计的底层逻辑1.1 基尔霍夫定律的硬件实现// STM32验证KVL定律&#xff08;ADC采样法&#xff09; void verify_kvl() {ADC_Enable(ADC1); // 启用ADCfloat Vr1 read_ADC(PA0) * 3.3 / 4096; // 读取R1电压float Vr2 read_ADC(PA1) * 3.3 / 4…

Linux网络:序列化与反序列化

引入&#xff1a;面向字节流 TCP是面向字节流的&#xff0c;如果按照字节流来读取信息&#xff0c;可能会出问题 比如客户传入“1100”&#xff0c;服务器读入“11”&#xff0c;后面的00被当作下一条信息&#xff0c;这就出问题了 我们可以将多个信息合并为一个字符串 在发送信…

二、Spark 开发环境搭建 IDEA + Maven 及 WordCount 案例实战

作者&#xff1a;IvanCodes 日期&#xff1a;2025年7月20日 专栏&#xff1a;Spark教程 本教程将从零开始&#xff0c;一步步指导您如何在 IntelliJ IDEA 中搭建一个基于 Maven 和 Scala 的 Spark 开发环境&#xff0c;并最终完成经典的 WordCount 案例。 一、创建 Maven 项目…

【python】算法实现1

实现一个动态规划算法 def dynamic_programming_example(n: int) -> List[int]:"""动态规划示例&#xff1a;计算斐波那契数列参数:- n: 斐波那契数列的项数返回:- List[int]: 斐波那契数列前n项"""if n < 0:return []elif n 1:return […

C++控制台贪吃蛇开发:从0到1绘制游戏世界

资料合集下载链接: ​​https://pan.quark.cn/s/472bbdfcd014​ 本文将带你一步步实现以下目标: 初始化游戏元素(边界、蛇、食物)的数据。 绘制静态的游戏边界(墙)。 在指定位置显示蛇和食物。 学习并使用Windows API来精确定位光标,实现“指哪打哪”的绘图。 隐藏闪烁…

共享模式、社群与开源链动2+1模式AI智能名片S2B2C商城小程序的协同发展研究

摘要&#xff1a;本文深入探讨了共享模式与社群之间的内在联系&#xff0c;指出信用体系完善是共享模式前提&#xff0c;信任源于相同认知促使共享在社群中更易发生。同时&#xff0c;引入开源链动21模式AI智能名片S2B2C商城小程序这一新兴元素&#xff0c;分析其在共享模式与社…

LeetCode 322. 零钱兑换 LeetCode 279.完全平方数 LeetCode 139.单词拆分 多重背包基础 56. 携带矿石资源

LeetCode 322. 零钱兑换 思路1&#xff1a; 回溯算法可以做&#xff0c;只要存储数组的最小长度即可&#xff0c;但可能会超时。思路2: 相当于是求最大价值的相反面&#xff0c;另外一个物品可以使用多次&#xff0c;因此是个完全背包。因此这是个完全背包的求最小价值类型题…

JAVA面试宝典 -《Elasticsearch 深度调优实战》

文章目录一、引言&#xff1a;搜索引擎为啥越来越慢&#xff1f;1.1 典型业务场景性能瓶颈表现​​&#xff1a;二、倒排索引压缩&#xff1a;让存储与检索更高效&#x1f9e0; 2.1倒排索引结构简述&#x1f527; 2.2 压缩算法三剑客✅ 调优建议三、分片策略&#xff1a;写入性…

克鲁斯焊接机器人保护气省气方案

在现代焊接工艺中&#xff0c;克鲁斯焊接机器人扮演着至关重要的角色。随着制造业对成本控制和可持续发展的日益重视&#xff0c;焊接过程中的保护气省气问题成为了焦点。WGFACS节气装置为克鲁斯焊接机器人的保护气省气提供了一种创新且有效的解决方案。克鲁斯焊接机器人以其高…

JavaEE——多线程中的哈希表

目录前言1.HashTable2.ConcurrentHashMap总结前言 在使用多线程前&#xff0c;我们用HashMap类来创建哈希表&#xff0c;但这个类线程不安全&#xff0c;在这篇文章&#xff0c;我们将介绍多线程环境的哈希表&#xff0c;将会讲述HashTable, HashMap, ConcurrentHashMap这三个…

MyBatis Plus SQL性能分析:从日志到优化的全流程实战指南

引言 在Java开发的江湖里&#xff0c;MyBatis Plus&#xff08;MP&#xff09;早已是“效率利器”——它用极简的API封装了CRUD操作&#xff0c;让开发者从重复的SQL编写中解放出来。但随着项目数据量从“万级”跃升至“十万级”“百万级”&#xff0c;一个尴尬的现实逐渐浮现&…

备忘录设计模式

备忘录模式&#xff08;Memento Pattern&#xff09;是一种行为设计模式&#xff0c;用于捕获对象的内部状态并在需要时恢复该状态&#xff0c;同时不破坏对象的封装性。它适用于需要实现撤销/重做、历史记录或状态快照的场景。核心组件Originator&#xff08;原发器&#xff0…

【世纪龙科技】智能网联汽车环境感知系统教学难题的创新实践​

在职业院校智能网联汽车专业教学中&#xff0c;环境感知系统的教学长期面临三大核心挑战&#xff1a;设备成本高昂导致实训资源不足、抽象原理难以直观呈现、传统教学模式难以满足产业需求。如何让学生在有限的教学条件下&#xff0c;深入理解激光雷达、毫米波雷达等核心部件的…

ES vs Milvus vs PG vector :LLM时代的向量数据库选型指南

互联网时代&#xff0c;关系型数据库为王。相应的&#xff0c;我们的检索方式也是精确匹配查询为主——查找特定的用户ID、商品编号或订单状态。但AI时代&#xff0c;语义检索成为常态&#xff0c;向量数据库成为搜索推荐系统&#xff0c;大模型RAG落地&#xff0c;自动驾驶数据…

磁盘阵列技术的功能与分类

磁盘阵列技术 磁盘阵列是由多台磁盘存储器组成的一个快速、大容量、高可靠的外存子系统。现在常见的磁盘阵列称为廉价冗余磁盘阵列&#xff08;Redundant Array of Independent Disk,RAID)。目前&#xff0c;常见的 RAID 如下所示。 廉价冗余磁盘阵列 RAID级别 RAID-0是一种不具…

SpringMVC核心注解:@RequestMapping详解

概述RequestMapping是SpringMVC中最核心的注解之一&#xff0c;用于将HTTP请求映射到MVC和REST控制器的处理方法上。基本功能RequestMapping主要用于&#xff1a;映射URL到控制器类或方法定义请求方法类型&#xff08;GET、POST等&#xff09;定义请求参数、请求头等条件使用位…

【杂谈】硬件工程师怎么用好AI工具做失效分析

最近被派到国外出差了&#xff0c;工作任务比较重&#xff0c;所以更新的频率比较低。但在出差工作的过程中&#xff0c;我发现在失效分析时&#xff0c;有相当多的时间做的是比较重复的工作。比如失效分析肯定要一些证据如图片、视频。当我们做多台设备的失效分析时&#xff0…

MyBatis详解以及在IDEA中的开发

MyBatis概述 MyBatis是一个优秀的持久层框架&#xff0c;它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集的过程。 核心特点 优势&#xff1a; SQL语句与Java代码分离&#xff0c;便于维护支持动态SQL&#xff0c;灵活性…

LangGraph教程6:LangGraph工作流人机交互

文章目录 Human-in-the-loop(人机交互) interrupt Warning Human-in-the-loop(人机交互) 人机交互(或称“在循环中”)工作流将人类输入整合到自动化过程中,在关键阶段允许决策、验证或修正。这在基于 LLM 的应用中尤其有用,因为基础模型可能会产生偶尔的不准确性。在合规、…

Linux部署Milvus数据库及Attu UI工具完全指南

一、准备工作1.1 环境要求操作系统&#xff1a;Ubuntu 20.04/Debian 11/CentOS 7硬件配置&#xff1a;至少8GB内存&#xff0c;4核CPU&#xff0c;50GB磁盘空间网络要求&#xff1a;可访问互联网&#xff08;用于拉取Docker镜像&#xff09;1.2 安装Docker和Docker Compose1.2.…