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

先上题目

在一个有序数组中,查找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;
}
  • 17
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值