首页 > 其他分享 >二分/二分查找(整数二分详解+拓展浮点二分)

二分/二分查找(整数二分详解+拓展浮点二分)

时间:2024-03-16 22:59:29浏览次数:24  
标签:二分 10 return int mid 浮点 详解 best

先上题目

在一个有序数组中,查找x所在的下标。

输入
第一行两个整数n和m。

第二行n个数,表示有序的数列。

接下来m行,每行一个整数x,表示一个询问的数。

输出
对于每个询问如果x在数列中,输出下标。否则输出-1

样例
输入 1
5 3
3 4 5 7 9
7
3
8
输出 1
4
1
-1
提示
对于100%的数据

n和m的范围[0,10^5];

x和数列中的元素范围[-10^6, 10^6];

数列中的任意两个元素都不相同。

【分析】很明显这是一道查找题,我们直接for一遍就行。但是,如果真能这样那就没有二分存在的必要了,for一遍的最坏的时间复杂度是O(m*n),就是O(10^5 * 10^5),这。。。确定不会超时吗,所以就到了我们今天的主题二分上场了

何为二分?

二分就像他的名字,是分一半,然后再分一半……,(如图)
在这里插入图片描述
二分是一种分治的思想,二分查找也是。二分的工作原理就是每次取中间,然后判断+调整,
无限逼近最终答案。
当然二分的前提是得有序,无论什么,都要有序
下面是二分的伪代码

void points(){
	int l=1,r=n,best=-1;   //左   右    答案
	while(l<=r)    //范围    当越过范围时就是答案
	{
		int mid=(l+r)/2;
		if(judge()){  //条件判断
			l=mid+1;   //逼近
			best=mid; //保存答案
		}
		else{
			r=mid-1;  //逼近
		}
	}
}

没错就这么短,却步步都是精髓,也非常实用,而且他的时间复杂度是 O(log n)

整数二分经典例题

既然已经理解二分了,那就先来个二分查找助助兴
在一个有序数组中,查找x所在的下标。

输入
第一行两个整数n和m。

第二行n个数,表示有序的数列。

接下来m行,每行一个整数x,表示一个询问的数。

输出
对于每个询问如果x在数列中,输出下标。否则输出-1

样例
输入 1
5 3
3 4 5 7 9
7
3
8
输出 1
4
1
-1
提示
对于100%的数据

n和m的范围[0,10^5];

x和数列中的元素范围[-10^6, 10^6];

数列中的任意两个元素都不相同。
【分析】为什么是刚开始的题目?咳咳。。。这不重要。这是一道简单的二分查找题,而且已经给出是有序数组,所以只需套下伪代码。不过仍需注意,二分只是逼近,并不是准确,所以二分时还要看情况特判。

#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N=1e5+10;
int a[N],d; 
int find(int x){
	int l=1,r=n,best=-1;
	while(l<=r){   //二分
		int mid=(l+r)/2;//取中间
		if(a[mid]>=x){   //大了或等于
			r=mid-1;   //变为前半部分
			best=mid;  //保存
		}
		else{   //小了
			l=mid+1;   //变为后半部分
		}
	}
	if(a[best]!=x) return -1;   //特判,万一没有
	return best;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
while(m--){
	cin>>d;
	cout<<find(d)<<"\n";
}
	return 0;
}

第二题

给你一个有序的整数序列,有一系列的询问,每次询问给出一个整数num,你需要回答序列中第一个等于num的位置,最后一个等于num的位置,第一个大于num的位置,如果相应的位置不存在,就输出-1

输入
第一行输入一个整数n (1<=n<=100000)

第二行输入n个整数ai, (1<=ai<=100000)

第三行输入一个整数m,表示询问的个数 (1<=m<=100000)

接下来m行每行一个整数bi,(0<=bi<=100000)

输出
对于每个询问输出三个整数

样例
输入 1复制
10
1 3 5 7 7 7 7 9 10 11
6
1
0
7
8
11
12
输出 1复制
1 1 2
-1 -1 1
4 7 8
-1 -1 8
10 10 -1
-1 -1 -1
【分析】简单又不太简单的经典二分(当然如果你用STL里的 lowerbound和upperbound我也没办法),要注意,这次的逼近,得分情况了,如果是求第一个等于num,就往前逼近,求最后一个就往后逼近,第一个大于num就不要保存等于了,保存大于,且往后逼近

#include<bits/stdc++.h>
using namespace std;
const int N=100050;
int n,a[N],m,b;
int find1(int x)     //找第一个等于
{
	int l=1,r=n,best=-1;
	while(l<=r)
	{
		int mid=(l+r)/2;
		if(a[mid]>=x){
			r=mid-1;
			best=mid;
		}
		else{
			l=mid+1;
		}
	}
	if(a[best]!=x) return -1;
	return best;
}
int find2(int x){   //最后一个等于
	int l=1,r=n,best=-1;
	while(l<=r){
		int mid=(l+r)/2;
		if(a[mid]<=x){
			best=mid;
			l=mid+1;
		}
		else{
			r=mid-1;
		}
	}
	if(a[best]!=x) return -1;
	return best;
}
int find3(int x){   //第一个大于
	if(a[1]>x) return 1;   //特殊情况
	if(a[n]<x) return -1;
	int l=1,r=n,best=-1;
	while(l<=r){
		int mid=(l+r)/2;
		if(a[mid]>x){   //不保存等于了
			r=mid-1;
			best=mid;
		}
		else{
			l=mid+1;
		}
	}
	return best;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
cin>>m;
while(m--){
	cin>>b;
	int k=find2(b);
	cout<<find1(b)<<" "<<k<<" "<<find3(b)<<"\n";
}
	return 0;
}

第三题(前方高能!!!)
【题目描述】
农夫 John 建造了一座很长的畜栏,它包括N(2≤N≤100,000)
个隔间,这些小隔间依次编号为x1,…,xN(0≤xi≤1,000,000,000)
. 但是,John的C(2≤C≤N)
头牛们并不喜欢这种布局,而且几头牛放在一个隔间里,他们就要发生争斗。为了不让牛互相伤害。John决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是什么呢

【输入】
第一行:空格分隔的两个整数N
和C;
第二行—第N+1行:i+1行指出了xi的位置。

【输出】
一个整数,最大的最小值。

【输入样例】
5 3
1 2 8 4 9
【输出样例】
3
【提示】
把牛放在1,4,8
这样最小距离是3

【分析】这一题还是有难度,首先得知道怎么办,二分!为什么?我们想想暴力是不是能做,但数据范围太大,那只能二分了。然后主要不知道二分啥,那我们想想,除了二分位置,距离,这题还能二分啥,二分位置也解不出来,只能二分距离,且是最大距离,所以这题有了:
1.排序数组不能忘
2.二分最大距离
3. 无限逼近

#include<bits/stdc++.h>
using namespace std;
int n,c;
const int N=100050;
long long x[N];
bool judge(long long mid)   //判断这个距离能否符合要求
{
	int cnt=1;
    long long th=x[1];   //上一个房屋
	for(int i=2;i<=n;i++){
		if(x[i]-th>=mid){    //距离是否大于mid
			th=x[i];   //更换上一个房屋
			cnt++;     //房屋++
		}
	}
	return cnt>=c;   //符合要求
}
int main(){
cin>>n>>c;
for(int i=1;i<=n;i++) cin>>x[i];
sort(x+1,x+n+1);  //排序
long long l=1,r=1e10,best=-1;
while(l<=r){  //二分最大距离
	long long mid=(l+r)/2;
	if(judge(mid)){   //判断
		l=mid+1;   //可以就继续
		best=mid;   //保存
	}
	else{
		r=mid-1;  //距离太大,缩小
	}
}
cout<<best;
	return 0;
}

拓展浮点二分

浮点二分相比于普通二分难比较了,而且也不能加1,减1了
这里给大家整理了下注意事项
1.浮点比较得用eps(其实就是0,为了防止精度不够,通常要射,如1e-6,就是把0保留了6位小数),或者限定次数
2.浮点二分通常用bool函数区比较,直接的话容易爆精度
3.浮点二分的精度设的大些,方爆精度的
好的有请例题
在x轴上有n个人,每个人有一个移动速度 v1,v2,v3…vn, 现在需要找一个地方让大家聚到一起开会,问你最少需要多少时间才可以让所有人都到达同一个点

输入
第一行输入一个整数n

第二行输入n个整数

x1,x2,x3…xn 表示每个人初始的位置

第三行输入n个整数

v1,v2,v3…vn表示每个人的移动速度

输出
输出一个浮点数,保留五位小数

样例
输入 1复制
3
7 1 3
1 2 1
输出 1复制
2.00000
输入 2复制
10
2 3 5 7 11 13 17 19 23 29
6 5 4 3 2 1 2 3 4 5
输出 2复制
2.75000
2≤n≤60000,1≤x i​ ,v i ≤10 ^ 9
【分析】这题我直接说了,是二分时间,至于为什么大家可以自己思考。二分时间的话这题就简单了

#include<bits/stdc++.h>
using namespace std;
const int N=60005;
int n,x[N],v[N];
const double eps=1e-6;
bool judge(double mid){
	double mi=-1e18;
	double mx=1e18;
	for(int i=1;i<=n;i++){
		double l=x[i]*1.0-v[i]*mid;   //最小的位置
		double r=x[i]*1.0+v[i]*mid;    //最大的位置
		if(l-mi>eps) mi=l;
		if(r-mx<-eps) mx=r;
	}
	if(mx-mi>=eps) return true;
	return false;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&x[i]);
for(int i=1;i<=n;i++) scanf("%d",&v[i]);
double l=1,r=1e9,best=-1;
int step=50;
while(step--){//二分次数
	double mid=(l+r)/2;
	if(judge(mid)){
		r=mid-1;
		best=mid;
	}
	else{
		l=mid+1;
	}
}
printf("%.5f",best);
	return 0;
}

标签:二分,10,return,int,mid,浮点,详解,best
From: https://blog.csdn.net/longxuan01/article/details/136766458

相关文章

  • ic基础|时序篇06:输入约束set_input_delay与输出约束set_output_delay详解
    大家好,我是数字小熊饼干,一个练习时长两年半的ic打工人。我在两年前通过自学跨行社招加入了IC行业。现在我打算将这两年的工作经验和当初面试时最常问的一些问题进行总结,并通过汇总成文章的形式进行输出,相信无论你是在职的还是已经还准备入行,看过之后都会有有一些收获,如果看......
  • Git常用命令详解
    目录前言操作环境准备工作Linux安装gitgit简介本地控制保证完整性Git一般只添加数据git大致流程本地仓库管理初始化git仓库存到暂存区.gitignore文件提交到本地仓库分支管理新建分支查看分支切换分支合并分支删除分支版本回滚远程仓库管理第三方远程仓库Gitee注册账......
  • Offer必备算法14_哈希表_五道力扣题详解(由易到难)
    目录①力扣1.两数之和解析代码②力扣面试题01.02.判定是否互为字符重排解析代码③力扣217.存在重复元素解析代码④力扣219.存在重复元素II解析代码⑤力扣49.字母异位词分组解析代码本篇完。①力扣1.两数之和1.两数之和难度简单给定一个整数数组 nu......
  • 详解MySQL的MVCC(ReadView部分解析C++源码)
    文章目录1.什么是MVCC2.MVCC核心组成(三大件)2.1MVCC为什么需要三大件3.隐藏字段4.undolog4.1模拟版本链数据形成过程5.ReadView5.1m_ids5.2m_creator_trx_id5.3m_low_limit_id5.4m_up_limit_id5.5可见性分析算法6.MVCC流程模拟6.1RC隔离级别6.2RR隔离......
  • 整数和浮点数在内存中的储存(包含原反补码的讲解)
    在c语言中,我们常常使用整数和浮点数,那么你知道整数和浮点数在内存中是如何储存的吗?下面大家一起学习。文章目录一.整数在内存中的储存二.了解大小端字节序三.浮点数在内存中的储存一、整数在内存中的储存整数的二进制表示方法有三种:原码、反码、补码。有符号整数......
  • C++类模板与友元详解
    C++模板下面分四种情况分别讨论。1.函数、类、类的成员函数作为类模板的友元函数、类、类的成员函数都可以作为类模板的友元。程序示例如下:void Func1() {  }class A {  };class B{public:    void Func() { }};template <class T>class Tmpl{......
  • C++类模板与继承详解
    C++模板类模板和类模板之间、类模板和类之间可以互相继承。它们之间的派生关系有以下四种情况。1.类模板从类模板派生示例程序:template <class T1, class T2>class A{    Tl v1; T2 v2;};template <class T1, class T2>class B : public A <T2,......
  • WPF中轻松操控GIF动画:WpfAnimatedGif库详解
    概述:在WPF中使用`WpfAnimatedGif`库展示GIF动画,首先确保安装了该库。通过XAML设置Image控件,指定GIF路径,然后在代码中使用库提供的方法实现动画控制。这简化了在WPF应用中处理GIF图的过程,提供了方便的接口来管理动画播放和暂停。当使用 WpfAnimatedGif 库在WPF中显示GIF图动......
  • FFmpeg命令视频音频转码参数详解
    前言全局说明FFmpeg命令转码参数详解一、参数1.1FFmpeg常用参数参数说明备注-ifilename指定输入文件(或直接写文件名,用|竖线分割),在Linux下当然也能指定:0.0(屏幕录制)或摄像头。-c:v指定视频编码器copy、libx265-crf指定视频质量,范围为0-51,0为无损,23......
  • 网络流建模之拆点,原理详解,OJ练习
    一、网络流建模之拆点1.1问题引入现有工厂s,仓库t,中转站若干,s到中转站有若干道路,中转站到仓库有道路若干,工厂要向仓库运输一定的货物,每条道路都有最大运输量限制,问最大运货量。1.2转化为网络流问题显然上述问题我们可以轻松的建模转化为网络流问题该流网络的最大流就......