二分
理解
- while(left<=right),答案一定在[left,right]中。
- 思想有兩種
- 通過縮小範圍來快速找到目標數的位置(應用1)--- 找到答案終止循環
- 不斷捨棄答案不可能存在的區間來逼近真實答案,若答案爲整數,則可以正好找到該答案,若爲實數則會找到在某個精度內的答案(應用2,3)--- 把循環全走一邊結束
STL中給出的函數
lower_bound
upper_bound
應用題型
- 查找(找到指定數字即可、有重複數字時查找第一個、找比指定數大的...)
- 求單調函數零點
- 最大化最小值、最小化最大值
- 快速冪
查找
1.查找指定數字
例題: Can you find it? HDU - 2141
- 二分模板
int bsearch()
{
int left = 0,right = n,mid;
while(left<=right)
{
mid = left+(right-left)/2;
if(a[mid]==x)
return mid;
else if(a[mid]<x)
left = mid+1;
else
right = mid-1;
}
return -1;
}
查找到就返回mid下標,沒找到就返回-1了。循環的終止是靠找到後return跳出,或者沒找到while判斷失敗結束。
模板的使用有幾處注意事項:
- 首先是mid = left+(right-left)/2,此處這麼寫是有原因的,若寫mid = (right+left)/2則在數組開的很大的時候,作爲下標,雖然數組本身下標可以用int 表示,但right和left相加容易int溢出。寫mid = left/2+right/2就更不行了,這個很有可能死循環,如left=1,right=1,mid=0(自己掉過的坑)。
- 還有就是循環條件和left、right的變換方式要匹配,要麼就全按照模板這樣寫,還有其它種寫法,若是整數的搜索,按照模板沒問題的,因爲模板會遍歷到所有數。詳細解釋:轉載
- 沒有常數步進容易造成死循環,詳細解釋:轉載
二分法的查找過程就是二叉排序(查找、搜索)樹
求單調函數零點
例題:Can you solve this equation? HDU - 2199
這種應用不是找到真正的解,而是在一定的精度範圍內就可以了,所以循環的終止並不是靠a[mid] = x,而是靠while的條件如right-left<1e8,因爲結果一定是在這個區間內的,所以當結果可能的取值範圍在 1e8 說明精度就在 1e8 了,就可以結束了。
最小化最大值
這種二分的使用,實際上是有技巧的枚舉,答案爲已知區間的整數,所以就可以將這個區間內的所有整數全判斷一遍,但這種方法肯定就爆炸了,所以要在枚舉的同時帶技巧--二分是把肯定不成立的區間從可能解中刪去