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

posted @ 2023-08-10 23:17  new-code  阅读(100)  评论(0)    收藏  举报