CSP初赛复习-25-二分查找STL-二分答案
二分查找STL
STL(standard template libaray - 标准模板库)提供在排好序的数组上进行二分查找的算法,lower_bound , upper_bound , binary_search
二分查找的时间复杂度是O(logn)的
1 查找target出现的第一个位置 lower_bound
第一个等于target的下标
target=3时,查询3第1次出现的下标

参考程序
#include <bits/stdc++.h>
using namespace std;
int main(){
int a[]={0,1,2,3,3,3,3,5,5,6};//测试数据总共有9个元素 其中包括4个3是重复元素
int size=sizeof(a)/sizeof(a[0]);//sizeof 计算字节数 通过数组的总字节数/第1个元素字节数计算数组元素个数
/*
lower_bound(a+n1,a+n2,target)
a+n1 起始 a+n2 结束位置 target 要查找的数字
[a+n1,a+n2) 前闭后开 例如 [0,4) 查找下标为0,1,2,3 ,返回target出现的第1个元素指针
*/
int *lb = lower_bound(a,a+size,3); //返回target出现的第1个元素指针
cout<<lb-a<<endl;//返回target出现的第1个元素指针-数组首指针 ,即 target元素的下标
}
/*
输出
3
*/
2 求第一个大于target的下标 upper_bound
求最后一个target元素下标+1
target=3时,查询第1个大于3的数的下标

参考程序
#include <bits/stdc++.h>
using namespace std;
int main(){
int a[]={0,1,2,3,3,3,3,5,5,6};//测试数据总共有9个元素 其中包括4个3是重复元素
int size=sizeof(a)/sizeof(a[0]);//sizeof 计算字节数 通过数组的总字节数/第1个元素字节数计算数组元素个数
/*
upper_bound(a+n1,a+n2,target)
a+n1 起始 a+n2 结束位置 target 要查找的数字
[a+n1,a+n2) 前闭后开 例如 [0,4) 查找下标为0,1,2,3 ,返回大于target的数组元素指针
*/
int *lb = upper_bound(a,a+size,3); //返回大于target的数组元素指针
cout<<lb-a<<endl;//返回大于target的数组元素指针-数组首指针 即数组target元素下标
}
/*
输出
7
*/
3 查找数组中元素是否存在 binary_search
参考程序
#include <bits/stdc++.h>
using namespace std;
int main(){
int a[]={0,1,2,3,3,3,3,5,5,6};//测试数据总共有9个元素 其中包括4个3是重复元素
int size=sizeof(a)/sizeof(a[0]);//sizeof 计算字节数 通过数组的总字节数/第1个元素字节数计算数组元素个数
/*
binary_search(a+n1,a+n2,target)
a+n1 起始 a+n2 结束位置 target 要查找的数字
[a+n1,a+n2) 前闭后开 例如 [0,4) 查找下标为0,1,2,3 ,如果数组对应值包括target,返回1 否则返回0
*/
bool ret = binary_search(a,a+size,3);
cout<<ret<<endl;
}
/*
输出
1
*/
二分答案
二分答案顾名思义,它用二分的方法枚举答案,并且枚举时判断这个答案是否可行
直接对答案进行枚举查找,接着判断答案是否合法。如果合法,就将答案二分进一步靠近,如果不合法,就接着二分缩小判断。这样就可以大大的减少时间。
优雅的暴力,将枚举复杂度降低
例题1 分巧克力 -整数二分

大致思路
1 根据给出的边长范围可知,切出的巧克力边长为1~100000之间的数
2 另题意需要满足边长时整数,所以切出的巧克力边长为1~100000之间的整数
3因此可以枚举1~100000之间的整数,是否满足分给k个小朋友,如果满足进行枚举是否有更大的
参考程序
#include<iostream>
using namespace std;
const int N=1e5+10;
//n块巧克力 k个小朋友 h[i]表示某个长方形的长 w[i]表示某个长方形的宽
int n,k,h[N],w[N];
/*
当切下的巧克力边长为m时,能不能满足k位小朋友
*/
bool check(int m){
long long c = 0;
for(int i = 0;i < n;i++){
c += (long long)(h[i] / m) * (w[i] / m);//一个长方形可以切几个边长为m的正方形
}
if(c >= k){//如果切的正方形c>=k 说明可以满足k个小朋友
return true;
}
return false;//前面没结束 说明不能满足k个小朋友
}
int main(){
scanf("%d%d",&n,&k);//输入n个块巧克力 和k个小朋友
for(int i = 0;i < n;i++){//逐一输入n块巧克力的长和宽
scanf("%d%d",&h[i],&w[i]);
}
int ans = 1;
int l = 1,r = 100000;//在1~100000之间的整数枚举
while(l <= r){
int m = l + (r - l) / 2;
if(check(m)){//满足 则进行向右缩小范围 看看有没有更大的
ans = m;//可能多次赋值 最后一定是可能的最大值
l = m + 1;
}else{//不满足缩小边长 向左缩小范围 用更小边长继续尝试
r = m - 1;
}
}
printf("%d",ans);
return 0;
}
例题2 切绳子 浮点数二分

大致思路
1 枚举访问再0~所有n段绳子长度之和
2 枚举使用左开右开区间 while判断条件类似while(L+1<R) 此时1是精度 保留2位一般判断使用4位
3 枚举的每个长度是否可以分成k段
参考程序
#include<bits/stdc++.h>
using namespace std;
int n,k;
double a[10005],L,R,mid;
char s[10];
/*
判断x长度是否够分成k段 true可以分成k段 false 不能分成k段
*/
bool check(double x){
int tot=0;
for(int i=1;i<=n;i++){
tot+=(int)(a[i]/x);//每段除长度向下取整 多出来的余数不能再使用
}
return tot>=k;//如果总段数>=k说明够分 否则不够分
}
int main(){
scanf("%d%d",&n,&k);//n条绳子 切割出k条
for(int i=1;i<=n;i++){//逐一输入n条绳子长度
scanf("%lf",&a[i]);
R+=a[i];//绳子总长度 作为有边界 每段不可能超出总长度
}
while(L+1e-4<R){//一般 保留2位小数计算是使用4位 后面四舍五入到3位
mid=(L+R)/2;
if(check(mid))L=mid;//可以分继续向右缩小区间
else R=mid;//向左缩小区间
}
sprintf(s+1,"%.3f",L);//特殊处理 四舍五入到3位
s[strlen(s+1)]='\0';//舍去第3位
printf("%s",s+1);//输出2位
return 0;
}
CSP初赛复习-25-二分查找-练习题
https://www.cnblogs.com/myeln/articles/17615705.html
作者:newcode 更多资源请关注纽扣编程微信公众号

从事机器人比赛、机器人等级考试、少儿scratch编程、信息学奥赛等研究学习

浙公网安备 33010602011771号