前言
二分搜索有两种模板,一种是找到有序数组中大于等于base
的最小值,另一种是找到有序数组中小于等于base
的最大值。
一、题目陈述
给定一个按照升序排列的长度为n的整数数组,以及 q 个查询。
对于每个查询,返回一个元素k的起始位置和终止位置(位置从0开始计数)。如果数组中不存在该元素,则返回“-1 -1”。
输入格式
第一行包含整数n和q,表示数组长度和询问个数。
第二行包含n个整数(均在1~10000范围内),表示完整数组。
接下来q行,每行包含一个整数k,表示一个询问元素。
输出格式
共q行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回“-1 -1”。
数据范围
1≤n≤100000
1≤q≤10000
1≤k≤10000
输入样例
6 3
1 2 2 3 3 4
3
4
5
输出样例
3 4
5 5
-1 -1
二、解决思路
使用二分搜索的两种模板,找到小于等于base的最大值和大于等于base的最小值,将这两个边界和base进行比较,如果他们都和base大小相等,则base存在,并且两个边界就是二分找到的两个边界值;否则base不存在。
三、代码实现
#include<iostream>
using namespace std;
const int N = 1e5+10;
int a[N];
int n,q,k;
int base;
// 找小于等于base的最大值
bool check_1(int mid) {
if (a[mid] >= base) return true;
else return false;
}
int bsearch_1(int l,int r) {
while(l<r) {
int mid =( l + r) >> 1;
if(check_1(mid)) r = mid;
else l = mid + 1;
}
return l;
}
// 找大于等于base的最小值
bool check_2(int mid) {
if (a[mid] <= base) return true;
else return false;
}
int bsearch_2(int l,int r) {
while(l<r) {
int mid = (l + r + 1) >> 1;
if(check_2(mid)) l = mid;
else r = mid - 1;
}
return l;
}
int main() {
cin>>n>>q;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<q;i++) {
cin>>k;
base = k;
int l = bsearch_1(0,n-1);
int r = bsearch_2(0,n-1);
if(a[l]==k&&a[r]==k) cout<<l<<' '<<r<<endl;
else cout<<-1<<' '<<-1<<endl;
}
return 0;
}
注意:浮点数二分不需要考虑边界编码方式的不同,AcWing790 数的三次方根
。
double n;
int main() {
cin>>n;
bool flag = false;
if(n<0) {
flag = true;
n = -n;
}
double l = 0 ;
double r = n;
while(r-l>=1e-10) {
double mid = (r+l)/2.0;
if(mid*mid*mid>=n) r=mid;
else l=mid;
}
if(flag) l = -l;
cout<<fixed<<setprecision(6)<<l;
return 0;
}
总结
典型的二分搜索的应用,注意两种二分模板在编码细节上的不同。