演算法題解----二分查詢(整數和浮點數)
以前電視上有一種節目,主持人給一種商品,讓參賽者猜其價格,參賽者猜一次之後主持人會提示參賽者猜的價格是高了還是低了。
主持人會給一個價格區間,這時候如果你是參賽者你會怎麼猜呢?從主持人給的價格開始猜嗎?
這樣的效率太低了,如果主持人給的區間是1-1000,這時候的複雜度就是O(n)
如果給我的話我會從主持人給的價格中位數開始猜測,我會從500元開始猜,然後主持人說這個是高了還是低了,高了我就猜250,低了我就猜750,。
這個時候我用的方法就是二分。
首先我先來介紹整數的二分查詢:
題目描述
給定一個按照升序排列的長度為n的整數陣列,以及q個查詢。
對於每個查詢,返回一個元素k的起始位置和終止位置(位置從
如果陣列中不存在該元素,則返回-1
。
輸入格式
第一行包含整數n和q,表示陣列長度和詢問個數。
第二行包含n個整數(均在1∼10000範圍內),表示完整陣列。
接下來q行,每行包含一個整數k,表示一個詢問元素。
輸出格式
共q行,每行包含兩個整數,表示所求元素的起始位置和終止位置。
如果陣列中不存在該元素,則返回-1
。
資料範圍
1≤n≤100000
1≤q≤10000
1≤k≤10000
輸入樣例:
6 3
1 2 2 3 3 4
3
4
5
輸出樣例:
3 4
5 5
-1 -1
-
int binary_search1(int l,int r) { while(l<r) {int mid = (l+r+1)/2; if(check(mid)) l = mid; else r = mid-1; } return l; }
另一種情況和上面這種很相似
不過區別是mid = (l+r)/2
if( check( mid ) ) r = mid ;
else l = mid +1 ;
int binary_search2(int l,int r) { while(l<r) { int mid = (l+r)/2; if(check(mid)) r = mid;else l = mid+1; } return l; }
再回到這題
這題是單調上升的序列
我們可以把答案區間[ l , r ] 左邊 [ 0, r ] 看成是 <= x 的 區間
右邊 [ l , n-1] 看成是 >= x 的區間
那麼我們只要兩次二分先找到l再找到r就可以了
# include <iostream> # include <cstdio> using namespace std; const int N=100010; int n,m; int a[N]; int main() { cin>>n>>m; for(int i=0;i<n;i++) scanf("%d",&a[i]); while(m--) { int k; cin>>k; int l=0,r=n-1; while(l<r) { int mid=(l+r)/2; if(a[mid]>=k) r=mid; else l=mid+1; } if(a[l]!=k) cout<<"-1 -1"<<endl; //如果左邊界沒有找到說明區間裡面沒有這個數 else { cout<<l<<" "; r=n-1; //重新給右區間賦值 while(l<r) { int mid=(l+r+1)/2; //一定要注意這裡的+1 死記住就可以了 if(a[mid]<=k) l=mid; else r=mid-1; } cout<<r<<endl; } } }
下面就介紹一下浮點數的二分,相對於整數二分來說反而簡單許多
題目描述
給定一個浮點數n,求它的三次方根。
輸入格式
共一行,包含一個浮點數n。
輸出格式
共一行,包含一個浮點數,表示問題的解。
注意,結果保留6位小數。
資料範圍
-10000≤n≤10000
輸入樣例:
1000.00
輸出樣例:
10.000000
這裡由於答案只有一個,所以你用binary_search1()或者 binary_search2()都可以
而且對於mid 都取 (l+r)/2
# include <iostream> # include <algorithm> # include <cstdio> using namespace std; double n; int main() { cin>>n; double l = -10000.0,r=10000.0; while(r - l > 1e-8) //小技巧,根據精度來確定迴圈條件 { double mid = (l+r) /2 ; if(mid*mid*mid >= n) r=mid; else l = mid; } printf("%.6f",l); //注意是輸出6位小數 }