【C++算法】二分算法、二分模板详解,四道例题带详细注释

news2024/11/15 5:14:07

文章目录

  • @[toc]
    • 1)整数二分
    • 2)解二分题步骤
      • AcWing 789.数的范围
      • 洛谷 P1873.EKO/砍树
      • 洛谷 P1678.烦恼的高考志愿
    • 2)浮点二分
      • AcWing 790. 数的三次方根

1)整数二分

  • 有单调性的题目一定可以二分,但是用二分做的题目不一定拥有单调性。
  • 二分的本质不是单调性,二分的本质是边界,两套模板如下:
  • 套模板时经常容易出现边界问题,那么什么时候选择哪套模板?听了y总的思路后,结合我自己的想法,接下来解析两套模板:
  • 首先想 c h e c k check check函数,再想如何更新区间,如果是 l = m i d l=mid l=mid,那么补上 + 1 +1 +1,如果是 r = m i d r=mid r=mid,那么不需要补上 + 1 +1 +1
  • ① 如果答案在右边,比如:找最大值,或者最后一个出现的位置;那么 l = m i d l=mid l=mid,那么对立面就是 r = m i d − 1 r=mid-1 r=mid1 m i d = l + r + 1 > > 1 mid= l+r+1>>1 mid=l+r+1>>1
  • ② 如果答案在左边,比如:找最小值,或者第一个出现的位置;那么$ r=mid$,那么对立面就是 l = m i d + 1 l=mid+1 l=mid+1 m i d = l + r > > 1 mid= l+r>>1 mid=l+r>>1
  • 对于第一种情况①,为什么要+1?可以想象一下,如果已经找到答案, m i d = l = r mid=l=r mid=l=r,那么 m i d = l + r > > 1 mid=l+r>>1 mid=l+r>>1就还是 [ l , r ] [l,r] [l,r],又因为包含答案,所以再次更新结果还是 [ l , r ] [l,r] [l,r],就会陷入死循环,也就是我们说的边界问题
  • 注意:二分一定是有解的,不可能无解,无解永远是题目的无解而不是二分的无解。
// 答案在左边,能取到答案
int bsearch_1(int l,int r) {
	while(l<r) {
		int mid=l+r>>1;
		if(check(mid))
			r=mid; // [l,mid]
		else
			l=mid+1; // [mid+1,r]
	}
	return l;
}

// 答案在右边,能取到答案
int bsearch_2(int l,int r) {
	while(l<r) {
        // 如何理解这个+1,见上述解析
		int mid=l+r+1>>1;
		if(check(mid))
			l=mid; // [mid,r]
		else
			r=mid-1; // [l,mid-1]
	}
	return l;
} 

2)解二分题步骤

  • 题目中出现求最值,首先想到二分/贪心/动态规划等算法

  • 题目具有单调性,则可以考虑用二分求解

  • 分析使用哪个模板

    1. 第一次出现,求第一个大于等于/大于/小于/小于等于某个数的数,求解最小值,说明答案在左边,用第一个模板

    2. 最后一次出现,最后一个大于等于/大于/小于/小于等某个数的数,求解最大值,说明答案在右边,用第二个模板

  • 写check函数

AcWing 789.数的范围

题目链接:789. 数的范围 - AcWing题库

在这里插入图片描述

#include<bits/stdc++.h>
#define x first
#define y second

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;

// 题目描述: 
// 找数字的左端点:大于等于x的第一个位置,q[mid]>=x,更新:R=mid,L=mid+1,当l=r时,若q[r]!=x,说明第一个大于等于x的位置不是x,则找不到
// 找数字的右端点:大于等于x的最后一个位置,q[mid]<=x,更新:L=mid,R=mid-1,当l=r时,若q[r]!=x,说明最后一个大于等于x

const int N=1e5+10;
int n,m;
int q[N];

int main() {
	cin>>n>>m;
	for(int i=0;i<n;i++) cin>>q[i];
	// m次询问
	while(m--) {
		int x;
		cin>>x;
		// 二分x的左端点
		int l=0,r=n-1; // 区间范围
		while(l<r) {
			int mid=l+r>>1;
			if(q[mid]>=x) r=mid;
			else l=mid+1;
		}
		if(q[r]==x) {
			// 存在左端点,输出
			cout<<r<<' ';
			
			// 继续找右端点,左端点继续从上一次搜索的终点开始找
			r=n-1;
			while(l<r) {
				int mid=l+r+1>>1;
				if(q[mid]<=x) l=mid;
				else r=mid-1;
			}
			cout<<r<<endl; // 输出一组解
		} else {
			cout<<"-1 -1"<<endl;	
		}
	}
	return 0;
}

洛谷 P1873.EKO/砍树

题目链接:[P1873 COCI 2011/2012 #5] EKO / 砍树 - 洛谷

#include<bits/stdc++.h>
#define x first
#define y second

using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;

// 解题思路: 

const ll N=1e6+10;
ll n,m; // n:树的数量,m:要的木材总长度
ll a[N]; // 存储树的高度

// 如果有木头有这么多,就返回true
bool check(ll mid) {
	ll cnt=0;
	for(ll i=1;i<=n;i++) {
		if(a[i]-mid>0) {
			cnt+=a[i]-mid;
		}	
	}
	if(cnt>=m) return true;
	else return false;
}

int main() {
	ll mmax=INT_MIN;
	cin>>n>>m;
	for(ll i=1;i<=n;i++) {
		scanf("%d",&a[i]);
		mmax=max(mmax,a[i]);
	}
	ll l=0; // 锯子最小高度为0,全切
	ll r=mmax; // 最高切完最大的这棵树,一棵都不切
	// 要找最大值,那么答案在右边,所以压缩左边界
	while(l<r) {
		ll mid=l+r+1>>1;
		if(check(mid)) {
			l=mid;
		} else {
			r=mid-1;
		}
	}
	cout<<l<<endl;
	return 0;
}

洛谷 P1678.烦恼的高考志愿

题目链接:P1678 烦恼的高考志愿 - 洛谷

#include<bits/stdc++.h>
#define x first
#define y second

using namespace std;

typedef long long ll;
typedef pair<int,int> PII;

// 解题思路: 开ll不然过不了

const ll N=1e5+5;
ll n,m;
ll a[N]; // 存储学校的分数线
ll b[N]; // 存储每个同学的估分

int main() {
	cin>>n>>m;
	// n个学校,m个同学
	for(ll i=1;i<=n;i++) {
		scanf("%lld",&a[i]);	
	}
	for(ll i=1;i<=m;i++) {
		scanf("%lld",&b[i]);
	}
	sort(a+1,a+1+n);
	ll cnt=0;
	// 遍历所有同学
	for(ll i=1;i<=m;i++) {
		// 卫语言风格
		// 比最小值还小,跳过
		if(b[i]<=a[1]) {
			cnt+=abs(b[i]-a[1]);
			continue;
		}
		else if(b[i]>=a[n]) {
			cnt+=abs(b[i]-a[n]);
			continue;
		}
		ll l=1,r=n; // 边界是[1,n]
		while(l<r) {
			ll mid=l+r>>1;
			// 注意找第一个是答案在左边的问题,所以要压缩右边界
			// 找第一个大于等于b[i]的第一个学校的分数线a[l]
			// 那么最后一个小于b[i]的元素的下标就应该是a[l-1]
			if(a[mid]>=b[i])
				r=mid;
			else
				l=mid+1;
		}
		// 取二者之中的最小值
		cnt+=min(abs(a[l]-b[i]),abs(a[l-1]-b[i]));
	}
	cout<<cnt;
	return 0;
}

2)浮点二分

AcWing 790. 数的三次方根

题目链接:790. 数的三次方根 - AcWing题库

  • 对于开二次方根,因为开出来一定是正数,所以可以设置 l = 0 l=0 l=0 r = x r=x r=x,但是三次方根可能有负数,不能单纯的取 l = − x l=-x l=x r = x r=x r=x,这样的话输入的 x x x是正数,范围是 [ − x , x ] [-x,x] [x,x],输入的数是负数,范围是 [ x , − x ] [x,-x] [x,x]就会出大问题。
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
int main() {
	double x;
	cin>>x;
	// 因为是开三次方根,所以要考虑负数的情况
    // 注意
	double l=-100000,r=100000;
	// 保留6位小数就1e-8(基于经验),同理保留4位就1e-6
	while(r-l>1e-8) {
		double mid=(l+r)/2;
		if(mid*mid*mid>=x)
			r=mid;
		else
			l=mid;
	}
	cout<<setprecision(6)<<fixed<<l<<endl;
	return 0;
}

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

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

相关文章

Linux初学(八)磁盘管理

一、磁盘管理 1.1 简介 磁盘的工作原理&#xff1a; 添加磁盘对磁盘进行分区格式化磁盘挂载和使用磁盘 磁盘的类型&#xff1a; 固态机械 磁盘的接口类型&#xff1a; IDESTSTSCSI 磁盘工作的原理&#xff1a; 磁盘&#xff0c;特别是硬盘&#xff0c;和内存不同&#xff0c;…

【Bug】记录2024年遇到的Bug以及修复方案

--------------------------------------------------------分割线 2024.3.22------------------------------------------------------- 1、load_sample_image raise AttributeError(“Cannot find sample image: %s” % image_name) AttributeError: Cannot find sample ima…

nvidia显卡如何安装cuda驱动

目录 查看显卡对应的cuda版本下载与你显卡匹配的CUDA Toolkit 查看显卡对应的cuda版本 按 微软 R 键&#xff0c;输入cmd 然后输入 nvidia-smi &#xff0c;回车显示下面信息&#xff1a; 看到 CUDA Version 为 12.2 下载与你显卡匹配的CUDA Toolkit 打开网页&#xff1a…

鸿蒙Harmony应用开发—ArkTS-@AnimatableExtend装饰器:定义可动画属性

AnimatableExtend装饰器用于自定义可动画的属性方法&#xff0c;在这个属性方法中修改组件不可动画的属性。在动画执行过程时&#xff0c;通过逐帧回调函数修改不可动画属性值&#xff0c;让不可动画属性也能实现动画效果。 可动画属性&#xff1a;如果一个属性方法在animation…

C++默认构造函数(二)

目录 构造函数补充 构造函数初始化列表的使用 赋值运算符重载函数 运算符重载函数介绍 运算符重载函数的使用 赋值运算符重载函数 赋值运算符重载函数的使用 拷贝构造函数和赋值运算符重载函数 重载前置和后置 前置 后置 重载流插入<<与流提取>> 流插…

C++ 其它

1、内存四区-代码区 2、内存四区-全局区 生成exe后&#xff0c;运行前是代码区和全局区 3、内存四区-栈区 4、内存四区-堆区 5、new *new一个整型10&#xff0c;返回的是该数据类型的指针&#xff0c;所以用int p 所以是int [10]&#xff0c;所以new的是int[10]&#x…

3.23项目:聊天室

1、 基于UDP的网络聊天室 项目需求&#xff1a; 如果有用户登录&#xff0c;其他用户可以收到这个人的登录信息如果有人发送信息&#xff0c;其他用户可以收到这个人的群聊信息如果有人下线&#xff0c;其他用户可以收到这个人的下线信息服务器可以发送系统信息 服务器 #inc…

东方博宜 1469. 数的统计

东方博宜 1469. 数的统计 #include<iostream> using namespace std; int main() {int n ;cin >> n ;int x ;cin >> x ;int cnt ;cnt 0;for (int i 1 ; i < n ; i){int num ;num i ;while(num!0){int g ;g num % 10 ;if (g x)cnt 1 ;num num / 10…

springboot294基于java的火车票订票系统的设计与实现

火车票订票系统设计与实现 摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装火车票订票系统软件来发挥其高效…

ElasticSearch首次启动忘记密码,更改密码(Windows 10)

先启动ElasticSearch 启动方式cmd到lasticsearch-8.12.2\bin目录下输入elasticsearch 启动成功后新开一个窗口输入elasticsearch-reset-password -u elastic

《剑指 Offer》专项突破版 - 面试题 88 : 动态规划的基础知识(C++ 实现)

目录 前言 面试题 88 : 爬楼梯的最少成本 一、分析确定状态转移方程 二、递归代码 三、使用缓存的递归代码 四、空间复杂度为 O(n) 的迭代代码 五、空间复杂度为 O(1) 的迭代代码 前言 动态规划是目前算法面试中的热门话题&#xff0c;应聘者经常在各大公司的面试中遇到…

C++ —— 日期计算器

1. 头文件 #pragma once #include <iostream> using namespace std;class Date { public:Date(int year 1, int month 1, int day 1);int GetMonthDay();bool operator>(const Date& d) const;bool operator>(const Date& d)const;bool operator<(c…

机器学习--jupyter-matplotlib使用中无法显示中文

jupyter使用中无法显示中文 在jupyter中&#xff0c;通过matplotlib作图时可能会添加中文标题&#xff0c;但有时候会不显示中文 import numpy as np import matplotlib.pyplot as pltx np.arange(0, 6, 0.1) # 以0.1为单位&#xff0c;成0到6的数据 y1 np.sin(x) y2 np.c…

ubuntu安装多个gcc并设置可切换

测试环境&#xff1a; Ubuntu16.04 1. 查看当前有几个gcc&#xff0c;g ls /usr/bin/gcc* ls /usr/bin/g* 有两个版本&#xff0c;5和7. 2. 安装特定gcc/g 版本 可以用sudo apt install gcc-version安装&#xff0c;比如说我想安装gcc-7&#xff0c;则命令为sudo apt instal…

第5章 数据建模和设计

思维导图 5.1 引言 最常见的6种模式&#xff1a;关系模式、多维模式、面向对象模式、 事实模式、时间序列模式和NoSQL模式 每种模式分为三层模型&#xff1a;概念模型、逻辑模型和物理模型 每种模型都包含一系列组件&#xff1a;如实体、关系、事实、键和属性。 5.1.1 业务驱…

【Flink】窗口实战:TUMBLE、HOP、SESSION

窗口实战&#xff1a;TUMBLE、HOP、SESSION 1.TUMBLE WINDOW1.1 语法1.2 标识函数1.3 模拟用例 2.HOP WINDOW2.1 语法2.2 标识函数2.3 模拟用例 3.SESSION WINDOW3.1 语法3.2 标识函数3.3 模拟用例 4.更多说明 在流式计算中&#xff0c;流通常是无穷无尽的&#xff0c;我们无法…

C++第十弹---类与对象(七)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、再谈构造函数 1.1、构造函数体赋值 1.2、初始化列表 1.3、explicit关键字 2、static成员 2.1、概念 2.2、特性 2.3、面试题 总结 1、再…

鸿蒙Harmony应用开发—ArkTS(@Prop装饰器:父子单向同步)

Prop装饰的变量可以和父组件建立单向的同步关系。Prop装饰的变量是可变的&#xff0c;但是变化不会同步回其父组件。 说明&#xff1a; 从API version 9开始&#xff0c;该装饰器支持在ArkTS卡片中使用。 概述 Prop装饰的变量和父组件建立单向的同步关系&#xff1a; Prop变量…

leetcode 2617. 网格图中最少访问的格子数【单调栈优化dp+二分】

原题链接&#xff1a;2617. 网格图中最少访问的格子数 题目描述&#xff1a; 给你一个下标从 0 开始的 m x n 整数矩阵 grid 。你一开始的位置在 左上角 格子 (0, 0) 。 当你在格子 (i, j) 的时候&#xff0c;你可以移动到以下格子之一&#xff1a; 满足 j < k < gri…

【单元测试】一文读懂java单元测试

目录 1. 什么是单元测试2. 为什么要单元测试3. 单元测试框架 - JUnit3.1 JUnit 简介3.2 JUnit 内容3.3 JUnit 使用3.3.1 Controller 层单元测试3.3.2 Service 层单元测试3.3.3 Dao 层单元测试3.3.4 异常测试3.3.5 测试套件测多个类3.3.6 idea 中查看单元测试覆盖率3.3.7 JUnit …