简介
二分查找又叫折半查找,他是有序线性表的一种查找算法。
算法思想
假定升序排列,下标从开始,二分查找函数如下,返回查找到的下标,查找失败返回。
/*二分查找*/
int bin_find(int *num,int len,int key){
int low,high,mid;
low=0;//查找下界
high=len-1;//查找上界
while(low<=high){
mid=(low+high)/2;//折半
if(key<num[mid])
high=mid-1;
else if(key>num[mid])
low=mid+1;
else
return mid;
}
return -1;//查找失败
}
例题
查找大于等于
题目描述
给定一个含有个元素的升序序列和次询问,每次询问一个数,输出序列中第一个大于等于该数的元素的位置,如果序列中没有符合条件的数,则输出"Not Found"(不输出引号)。
输入格式
第1行输入个整数和,表示序列长度和询问次数;
第行输入个用空格隔开的整数,表示这个序列;
接下来输入行,每行输入个整数 ,表示要询问的数;
输出格式
对于每组数据中的每次询问,输出一行:如果序列中第一个大于等于该数的元素存在,则输出其位置,否则输出"Not Found"(不包括引号)。
样例输入
5 4
1 2 3 3 5
1
3
4
6
样例输出
1
3
5
Not Found
题解
标准的二分查找变形即可,下标从开始,注意可能有重复,查找到合适下标后需要前移到第一次出现的下标。
#include <iostream>
using namespace std;
int bin_find(int *num,int len,int key){
int low,high,mid;
low=1;//查找下界
high=len;//查找上界
while(low<=high){
mid=(low+high)/2;//折半
if(key<num[mid])
high=mid-1;
else if(key>num[mid])
low=mid+1;
else
break;
}
while(mid<=len&&num[mid]<key) mid++;//只有大于,下标右移
while(mid>1&&num[mid-1]==num[mid]) mid--;//取等,下标左移
return mid;
}
int main(){
int n,q,num[100001],x;
cin>>n>>q;//输入长度和询问次数
for(int i=1;i<=n;i++) cin>>num[i];
while(q--){
cin>>x;//输入查询的x
int idx=bin_find(num,n,x);
if(idx!=n+1) cout<<idx<<endl;
else cout<<"Not Found\n";//等于长度+1,查找失败
}
return 0;
}
数的范围
题目描述
给定一个按照升序排列的长度为的整数数组,以及个查询。
对于每个查询,返回一个元素的起始位置和终止位置(位置从开始计数)。
如果数组中不存在该元素,则返回。
输入格式
第一行包含整数和,表示数组长度和询问个数。
第二行包含个整数(均在范围内),表示完整数组。
接下来行,每行包含一个整数k,表示一个询问元素。
输出格式
共行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回。
输入样例
6 3
1 2 2 3 3 4
3
4
5
输出样例
3 4
5 5
-1 -1
数据范围与提示
题解
标准的二分查找修改,找到下标后进行左移右移。
#include <iostream>
using namespace std;
int s,e;//左右下标
void bin_find(int *num,int len,int key){
int low,high,mid;
low=0;//查找下界
high=len-1;//查找上界
while(low<=high){
mid=(low+high)/2;//折半
if(key<num[mid])
high=mid-1;
else if(key>num[mid])
low=mid+1;
else{
s=e=mid;
break;
}
}
if(num[mid]!=key){//查找失败
s=e=-1;
return;
}
while(s>0&&num[s-1]==key) s--;//找左边界
while(e<len-1&&num[e+1]==key) e++;//找右边界
}
int main(){
int n,q,num[100001],x;
cin>>n>>q;//输入长度和询问次数
for(int i=0;i<n;i++) cin>>num[i];
while(q--){
cin>>x;//输入查询的x
bin_find(num,n,x);
cout<<s<<' '<<e<<endl;//输出下标范围
}
return 0;
}
数的三次方根
题目描述
给定一个浮点数,求它的三次方根。
输入格式 共一行,包含一个浮点数。
输出格式 共一行,包含一个浮点数,表示问题的解。
注意,结果保留位小数。
输入样例
1000.00
输出样例
10.000000
数据范围与提示
题解
采用二分法进行数据逼近,需要注意根可能是负数。
#include <iostream>
#include <cmath>
using namespace std;
double solve(double value){
double low=0,high=value,mid;
while(fabs(high)-fabs(low)>=1e-7){//精度到达10^(-7)时停止
mid=(low+high)/2;
if(abs(mid*mid*mid)>abs(value)){//mid大于三次方根
high=mid;
}else{//mid小于等于三次方根
low=mid;
}
}
return mid;
}
int main(){
double value;
cin>>value;
printf("%.6lf",solve(value));//6位小数
return 0;
}
## 砍树
题目描述
伐木工人米尔科需要砍倒米长的木材。这是一个对米尔科来说很容易的工作,因为他有一个漂亮的新伐木机,可以像野火一样砍倒森林。不过,米尔科只被允许砍倒单行树木。
米尔科的伐木机工作过程如下:米尔科设置一个高度参数(米),伐木机升起一个巨大的锯片到高度,并锯掉所有的树比高的部分(当然,树木不高于米的部分保持不变)。米尔科就行到树木被锯下的部分。
例如,如果一行树的高度分别为,,和,米尔科把锯片升到米的高度,切割后树木剩下的高度将是,,和,而米尔科将从第棵树得到米,从第棵树得到米,共得到米木材。
米尔科非常关注生态保护,所以他不会砍掉过多的木材。这正是他为什么尽可能高地设定伐木机锯片的原因。帮助米尔科找到伐木机锯片的最大的整数高度,使得他能得到木材至少为米。换句话说,如果再升高米,则他将得不到米木材。
输入格式
第行:个整数和,表示树木的数量(),表示需要的木材总长度()
第行:个整数表示每棵树的高度,值均不超过。所有木材长度之和大于,因此必有解。
输出格式
第行:个整数,表示砍树的最高高度。
样例输入
5 20
4 42 40 26 46
样例输出
36
题解
假设函数是砍树后得到的木材总长度,则f(h)单调递减,可以采用二分法求得临界值使得且。由于需要取最大的,所以需要修改。
#include <iostream>
using namespace std;
typedef long long ll;
ll tree[1000000];
int n,m;//n棵树,需要m米
ll f(ll h){
ll sum=0;
for(int i=0;i<n;i++){
if(tree[i]>h){//取等差值为0,省略等于
sum+=tree[i]-h;
}
}
return sum;
}
int main(){
ll low=1,high=1,mid;//查找边界
cin>>n>>m;
for(int i=0;i<n;i++){
cin>>tree[i];//输入
if(tree[i]>high) high=tree[i];//记录最大高度
}
while(low<high){
mid=(low+high+1)/2;//+1取尽量大
if(f(mid)>=m) low=mid;//保证low能用
else high=mid-1;//high减小
}
cout<<low;
return 0;
}
进击的奶牛
题目描述
Farmer John 建造了一个有个隔间的牛棚,这些隔间分布在一条直线上,坐标是。
他的头牛不满于隔间的位置分布,它们为牛棚里其他的牛的存在而愤怒。为了防止牛之间的互相打斗,Farmer John 想把这些牛安置在指定的隔间,所有牛中相邻两头的最近距离越大越好。那么,这个最大的最近距离是多少呢?
输入格式
第行:两个用空格隔开的数字和。
第行:每行一个整数,表示每个隔间的坐标。
输出格式
输出只有一行,即相邻两头牛最大的最近距离。
样例输入
5 3
1
2
8
4
9
样例输出
3
题解 以隔间距离为搜索值进行二分查找,搜索是否存在个隔间满足该距离。
#include <iostream>
#include <algorithm>
#define N (int)1e5
using namespace std;
typedef long long ll;
int check(int x[],int n,int c,int mid){//计算是否有c个隔间满足
int cnt=1,pre=x[0];//cnt是隔间数,至少是1,pre是上一个隔间坐标
for(int i=1;i<n;i++){//坐标从x[1]开始
if(x[i]-pre>=mid){//坐标距离满足
cnt++;
pre=x[i];
}
if(cnt==c) return 1;//满足返回1
}
return 0;//不满足返回0
}
int main(){
int n,c,low,high,mid;
int x[N];
cin>>n>>c;
for(int i=0;i<n;i++){
cin>>x[i];
}
sort(x,x+n);//坐标排序
low=0,high=x[n-1]-x[0];//隔间最小距离和最大距离
while(low<high){
mid=(low+high+1)/2;
if(check(x,n,c,mid)){//检查mid是否能满足
low=mid;
}else{
high=mid-1;
}
}
cout<<low;
return 0;
}
一元三次方程求解
题目描述
有形如:这样的一个一元三次方程。
给出该方程中各项的系数(,,,均为实数),并约定该方程存在三个不同实根(根的范围在至之间),且根与根之差的绝对值。
要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后位。
提示:记方程,若存在个数和,且,,则在之间一定有一个根。
输入格式
一行,个实数
输出格式
一行,个实根,并精确到小数点后位。
样例输入
1 -5 -4 20
样例输出
-2.00 2.00 5.00
题解
题目规定根在之间且根绝对值之差,则可以在中循环整数,不取是因为方便计算区间,对每一个长度为的小区间进行二分法求根,根据零点定理判定区间是否有根。
#include <iostream>
using namespace std;
typedef long long ll;
double a,b,c,d;
double f(double x){
return a*x*x*x+b*x*x+c*x+d;//三次函数
}
double bin_search(double low,double high){
double mid;
while(high-low>1e-3){
mid=(low+high)/2;
if(f(mid)*f(low)<0){//low~mid之间
high=mid;
}else if(f(mid)*f(high)<0){
low=mid;
}else{
return mid;
}
}
return mid;
}
int main(){
cin>>a>>b>>c>>d;
for(double i=-99;i<=100;i++){
if(f(i-1)==0){//i是一个根
printf("%.2lf ",i-1);
continue;
}
if(f(i-1)*f(i)<0){//有根
printf("%.2lf ",bin_search(i-1,i));
}
}
return 0;
}
版权声明
- 本文档归cout0所有,仅供学习使用,未经允许,不得转载。