今天要介绍的是一种比较常见但是效率很高的算法,就是二分法。因为二分法有着O(logn)的优秀时间复杂度,所以我们常常会用到它。
但是呢二分主要是运用于已经排好序了的序列,通常我们知道一个答案区间,然后二分的去寻找符合题意的answer;或者我们已知一个序列,我们要通过二分的方法去寻找它的位置,或者说是起始位置和终止位置。
二分呢,一般可以分为整数二分和实数二分,我们将会先介绍整数二分。
二分的思想很简单,通过不断的调整左右边界,使之逼近目标值。
整数二分最需要注意的问题就是边界问题,我们常常会因为边界问题思考过于粗糙,导致我们的程序陷入死循环或者某些情况未考虑导致答案错误。
接下来上板子:
//lower_bound Binary Search
int l=0,r=n-1;
while(l<r){
int mid=l+r>>1;
if(arr[mid]>=x) r=mid;
else l=mid+1;
}
//upper_bound Binary Search
int l=0,r=n-1;
while(l<r){ //保证了最后得到的下标l==r
int mid=l+r+1>>1; //这里加一是为了避免l+1==r时陷入死循环
if(arr[mid]<=x) l=mid;
else r=mid-1;
}
//有这么一道模板题:789. 数的范围 - AcWing题库
然后我们再看到实数二分,实数二分我们比较要多考虑的就是答案所要求的几位小数(有效数字),以及循环条件满足时边界该如何移动。
int main()
{
double n;
cin>>n;
double l=-10000,r=10000;
double mid;
while(r-l>=1e-7){ //这是因为要保留六位小数,所以误差值取为1e-7
mid=(l+r)/2; //这里就不用考虑加1了
if(mid*mid*mid>=n) r=mid;
else l=mid; //边界也变为mid而不是mid+1,因为是实数除法,不会陷入死循环
}
printf("%.6lf",mid);
return 0;
}
总之二分的例题有很多,如下(我之后遇到一些好题也会放上去的):
2.Problem - 1626C - Codeforces
3.Problem - 1632C - Codeforces
4.Problem - 1628A - Codeforces
5.4297. 截断数组 - AcWing题库 //这道题是在一个数组中去枚举去二分
6.D-[NOIP2001]一元三次方程求解_河北经贸大学2021-2022寒假第三次专题赛 (nowcoder.com)//实数二分
7.1659. 社交距离 I - AcWing题库 //贪心加二分