二分主要应用于最小化最大值和最大化最小值;
边界:
我的习惯是半闭半开的写法;
即答案在[ l , r )的范围中或答案在( l , r ]的范围中;
注意是答案在[ l , r )中;
所以 l 并不一定永远等于0或1,它是答案的左边界,而r是答案的右边界+1;
另一种情况同理;
所以说,有时候你写的二分是错误的…..只是没有坑爹的数据卡你……
最小化最大值
答案范围 : ( l , r];
例题:https://www.luogu.org/problemnew/show/1182
这道题的左边界是区间的最大值,而不是 1或 0;
如 : 5 5 1 2 3 7897979 4
输出:7897979;
所以l一定要设为最大值-1;
l--;
while(r - l >1)
{
int mid=(l+r) >> 1;
if(check(mid)) r=mid;//如果可行,看是否可以继续缩小最大值
else l=mid;
}
退出条件是 l + 1 == r
答案是 r ;
最大化最小值
答案范围 : [ l , r );
例题:NOIP2012 跳石头
这道题有个数据是:
65343245 0 0 (没有石头=_=,好坑啊);
输出 :65343245
r一定要设为总距离+1,因为r并不是你的答案右边界,而是答案右边界+1;
r++;
while(r - l > 1)
{
int mid=l+r >>1;
if(check(mid)) l=mid;//如果可行,看是否可以增加最小值;
else r=mid;
}
退出条件 : l + 1 == r
答案是 l
总结
1、对于半闭半开的写法,总是有一方的初始值为答案边界+1或-1;
2、注意以上两种写法是不同的;
小技巧?
如果不知道答案边界或者不知道输出l还是r怎么办?(好像并不存在<-_<-);
1、可以对r设一个极大值;
2、输出答案是将边界check一下,输出返回true的那个,
例如 : 跳石头;
如果边界设为l=0,r=sum;
加上这句话,那个坑爹的点也能过;
if(check(r)) l=r;//检查二分的上界是否满足;
cout<<l;
3、二分时保存一下答案,仅限于左闭右闭的写法;
希望大家有所收获~ ~ ~ ~ ~ ~ ~ ~