整数二分的目的:
查找
整数二分的思想:
每次留下一半,保证答案在这一半里
整数二分实现的具体步骤:
(1)根据问题分析,确定是左区间还是右区间,mid在哪个区间
(2)根据区间,判断l与r的取值,再根据l与r的取值,确定mid是否有+1
(3)二分的while循环的终止条件是l==r,即为答案。
bool check(int mid)
{
}
//两个整数二分模板,用哪个具体问题具体分析
//[l,mid]用来找l,求左边界,左区间
int bsearch_1(int l,int r)
{
while(l<r)
{
int mid=l+r >>1;
if(check(mid)) r=mid;//check判断mid是否满足某种性质
else l=mid+1;
}
return 1;
}
//[mid,r]用来找r,求右边界,右区间
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 1;
}
写一个check函数,根据check判断如何更新l或r,两种模板的区别在于是否有+1
1.对于r = mid的情况,避免区间不变,中间值需使用(l + r) >> 1,另一个分支一定是l = mid + 1。
2.对于l = mid的情况,避免区间不变,中间值需使用(l + r + 1) >> 1,另一个分支一定是r = mid - 1。
上面两种情况mid的取值实际一个是下取整,另一个是上取整,原因是为了避免区间不变,导致死循环。解释如下:
例题:Acwing 789. 数的范围
解答:
/*
有单调性必定可以二分,可以二分的不一定有单调性
测试用例
6 3
1 2 2 3 3 4
3
4
5
*/
#include<iostream>
#include<algorithm>
using namespace std;
const int N=10010;
int t[N];
int main()
{
int n,q;
cin>>n>>q;
for(int i=0;i<n;i++)
{
cin>>t[i];
}
for(int i=0;i<q;i++)
{
int k;
cin>>k;
int l=0,r=n-1;
while(l<r)//找起始位置,在左区间
{
int mid= l+r>>1;
//应该更新成[l,mid]
if(k<=t[mid])r=mid;
else l=mid+1;
}
if(t[l]!=k) cout<<"-1 -1"<<endl;
else
{
cout<<l<<' ';
while(l<r)//找终点,在右区间
{
int mid=l+r+1 >>1;
if(k>=t[mid])l=mid;
else r=mid-1;
}
cout<<r<<endl;
}
}
return 0;
}