二分:
二分不是二分,是二分。就是一分為二的二分。
先來一個例子:
現在有一個遞增的序列 a(1), a(2)...a(n),然后讓你查找 x 在不在這個序列里面?
顯然最簡單的做法就是一個for循環,從1到n,看看有沒和x相等的。。。
這樣確實不錯,但是太慢了。。。需要n次才能找到。有沒更好的做法呢?
有(要是沒有的話我說這個干什么),那就是二分查找了。
首先判斷 a(n/2) 和 x 誰大誰小,如果 x 大的話,那么顯然x可能在 a(n/2+1) 到 a(n) 這個范圍里面,而一定不會在 a(1) 到 a(n/2) 這個范圍里面,因為遞增嘛。。。然后再按同樣的方法遞歸查找后半個區間就行了。
如果x小的話同理找前半個區間。
這樣每次把區間變成原來的一半,那么就是 logN 級別的時間復雜度,對於長度 n=100000 的序列,只需要不到20次就能判斷x了。。。
具體代碼如下:
// 查找A數組是否存在x。 bool find(int A[],int N,int x) { int L=0,R=N-1,M; // left right middle while(R>L) { M=(L+R)/2; if(A[M]==x) return 1; else if(A[M]>x) R=M-1; else L=M+1; } if(A[L]==x) return 1; return 0; }
通俗的說,其實二分是針對一個單調函數,在這個函數的一個區間中查找,每次取區間的中點去判斷,然后根據大小把區間變成原來的一半。。。然后一直這樣下去就好。
二分的應用可以說是很廣很廣。
有一個典型的應用,就是最大值最小化問題,這類問題一般是要求一個方案讓所有的數里面最大的那個最小,然后求這個最小數是多少。
如果是直接硬解的話一般會很困難。所以需要轉換思路。
用二分來試一下。如果先給出一個上限M之后,要求所有數都不大於M,然后如果存在一種方案的話,說明最后的答案一定比這個數要小於等於,所以二分下去一次次逼近答案就OK了。
題目的話 UVA 714 就是經典的二分+貪心問題。
三分:
如果說二分針對的是單調函數,那么三分針對的是雙調函數。也就是下面這樣的函數。
三分是求這樣先增后減或者是先減后增的函數的極值的。
具體步驟就是:對於區間 (L,R),先求出 1/3 處的 M1 和 2/3 處的 M2,然后比較 M1 和 M2 處的大小,如果 M1 處的值大於 M2 處的值,那么極值一定在(M1,R)這個區間范圍內,否則一定在 (L,M2)這個范圍內。
因為如果 M1處大於M2處的話,M1不可能在極值點的右邊。。。所以。。。就是這樣了。。。每次變成原來區間的2/3大小。。。
二分三分的用處很廣很廣,比如對一個符合的序列,或者是在計算幾何中用來求一些不好算的值等等。。。
比如求點到橢圓的最近距離的題目,就可以通過三分一次次的逼近。