二分
二分本质是用来查找满足某种性质的边界点
在给定区间内,能找到某种性质(check函数):
使得区间分为两部分
一部分满足,另外一部分不满足这种性质。
二分可以用来寻找这个性质的边界:满足某种性质的第一个元素;或者是满足某种性质的最后一个元素。
从而有两个不同模板
注意二分法一定能找到满足某种性质的边界点,但它不一定是你要找的目标,目标一不定在区间内,先写check函数,根据check函数更新区间,再判断用哪个模板。
二分模板一共有两个,分别适用于不同情况。
算法思路:
假设目标在闭区间[l,r]中,每次将区间长度缩小一半,当l=r时,我们就找到了目标值。
版本1
当我们将区间[l, r]划分成[l, mid]和[mid + 1, r]时,其更新操作是r = mid或者l = mid +1;,计算mid时不需要加1。
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
return l;
}
版本2
当我们将区间[l, r]划分成[l, mid - 1]和[mid, r]时,其更新操作是r = mid - 1或者l =mid;,此时为了防止死循环,计算mid时需要加1。
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
AcWing 789.数的范围
#include<iostream>
using namespace std;
const int N=1e5+10;
int q[N];
int n,m;
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)
cin>>q[i];
while(m--)
{
int k;
cin>>k;
int l=0,r=n-1;
while(l<r)
{
int mid=(l+r)>>1;
if(q[mid]>=k)
r=mid;
else
l=mid+1;
}
if(q[l]!=k)
cout<<-1<<' '<<-1<<endl;
else
{
cout<<l<<' ';
int l=0,r=n-1;
while(l<r)
{
int mid=(l+r+1)>>1;
if(q[mid]<=k)
l=mid;
else
r=mid-1;
}
cout<<l<<' '<<endl;
}
}
return 0;
}
AcWing 790.数的三次方根
#include<iostream>
using namespace std;
int main()
{
double x;
cin>>x;
double l=-100,r=100;//根据题目范围 开三次方根 估计答案大概范围
while(r-l>1e-8)
{
double mid=(l+r)/2;
if(mid*mid*mid>=x)
r=mid;
else
l=mid;
}
printf("%.6lf\n",l);
return 0;
}
浮点数的平方根
#include<iostream>
using namespace std;
int main()
{
double x;
cin>>x;
double l=0,r;
//一个浮点数开平方,答案绝对值一定大于等于0,但是右边界根据输入浮点数是否大于1确定
if(x>1)
r=x;
else
r=1;
while(r-l>1e-8)//经验: 保留6位小数 1e-8 4位 1e-6 多2
{
double mid=(l+r)/2;
if(mid*mid>=x) r=mid;
else l=mid;
}
printf("%.6lf\n",l);
return 0;
}