C++ STL的二分查找函數:ios
binary_search 返回bool值,是否存在。c++
lower_bound 返回可插入的最小值的迭代器,即返回第一個符合條件的元素位置。(從已排好序的序列a中利用二分搜索,找出ai>=k的ai的最小指針)git
upper_bound 返回可插入的最大位置的迭代器,即返回最後一個符合條件的元素位置。(ai>k)promise
例:CF706Bide
/*binary_search本身第一次寫的*/
#include <iostream> #include <cstdio> #include <cmath> #include <algorithm> #include <string> #include <cstdlib> using namespace std; const int maxn = 100005; int a[maxn],b[maxn],m; int binary(int x) { int low=0,hight=m-1; int mid; mid=(hight-low)/2+low; while(low<=hight) { if(x==a[mid]&&a[mid+1]!=a[mid]) return mid+1; else if(x<a[mid]) hight=mid-1; else low=mid+1; mid=(hight-low)/2+low; } } int main() { int i,q; //freopen("Atext.in","r",stdin); cin >> m; for(i=0;i<m;i++) cin >> a[i]; cin >> q; sort(a,a+m); for(i=0;i<q;i++) cin >> b[i]; for(i=0;i<q;i++) { if(b[i]>=a[m-1]) cout << m << endl; else cout << binary(b[i]) << endl; } return 0; }
1.其中,若是尋找的value存在,那麼lower_bound返回一個迭代器指向其中第一個這個元素。upper_bound返回一個迭代器指向其中最後一個這個元素的下一個位置(明確點說就是返回在不破壞順序的狀況下,可插入value的最後一個位置)。若是尋找的value不存在,那麼lower_bound和upper_bound都返回「假設這樣的元素存在時應該出現的位置」。(= =槓掉的部分是我看了個假視頻)。函數lower_bound()在first和last中的前閉後開區間進行二分查找,返回大於或等於val的第一個元素位置。若是全部元素都小於val,則返回last的位置,且last的位置是越界的!!要指出的是lower_bound和upper_bound在源碼中只是變換了if--else語句斷定條件的順序,就產生了最終迭代器位置不一樣的效果。函數
2.binary_serach試圖在已排序的[first,last)中尋找元素value,若存在就返回ture,若不存在則返回false。返回單純的布爾值也許不能知足需求,而lower_bound,upper_bound能提供額外的信息。事實上由源碼可知,binary_serach即是利用lower_bound求出元素應該出現的位置,而後再比較該位置的值與value值。該函數有兩個版本一個是operator<,另一個是利用仿函數comp進行比較。this
/*p是嚴格不重複單調遞增的序列 q是小明猜的數字,隨機有重複 對每個q在p中進行查找,找到相同的就記錄該值 */ #include <iostream> #include <algorithm> #include <cstring> #define maxn 1000005 using namespace std; int p[maxn],q[maxn]; int main() { long long n,m,i,sum=0; memset(p,0,sizeof(p)); memset(q,0,sizeof(q)); cin >> n >> m; for(i=1;i<=n;i++) //從1開始,由於lower_bound與upper_bound必定會返回一個值,防止查找不到時越界 cin >> p[i]; for(i=1;i<=m;i++) //O(n) { cin >> q[i]; if(q[i]==p[lower_bound(p,p+n,q[i])-p]) //O(logn) sum+=q[i]; } cout << sum << endl; return 0; }
/*查找x在一個數列a中重複出現的次數*/ #include <iostream> #include <algorithm> #include <cstring> #define maxn 100005 using namespace std; int a[maxn]; int main() { int n,m,Q,i; while(cin >> n >> m) { for(i=1;i<=n;i++) cin >> a[i]; sort(a,a+n); for(i=0;i<m;i++) { cin >> Q; int k=upper_bound(a+1,a+n+1,Q)-lower_bound(a+1,a+1+n,Q); cout << k << endl; } } return 0; }
3.應用:二分答案+檢驗。對於難以直接肯定解的問題,採起二分枚舉+檢驗的思想將求解類問題轉換爲驗證類問題。spa
例:POJ1064 http://poj.org/problem?id=1064指針
題目大意:有n條繩子,它們的長度爲Li,從它們中切割出k條相等長度繩子,這k條繩子每條最長能有多長;code
像這樣在求解最大化或最小化問題中,可以比較簡單的判斷條件是否知足,那麼使用二分枚舉答案就能夠很好的解決問題;
Cable master
Description
Input
Output
Sample Input
4 11 8.02 7.43 4.57 5.39
Sample Output
2.00
#include <iostream> #include <cstdio> #include <cmath> using namespace std; const double eps=0.0001; const int maxn=10005; int n,k; double L[maxn]; bool cc(double x) //對於枚舉的x的長度,判斷其是否知足k個的要求; { int cnt=0; for(int i=0;i<n;i++) cnt+=(int)(L[i]/x); return cnt>=k; } void solve() { double l=0,r=200005; //題目是1m--100km,這裏又WA了一回; while(r-l>=eps){ //能夠把終止條件設爲一個指定的區間大小,也能夠設爲循環次數,但注意不要由於精度太高而死循環; double mid=(l+r)/2.0; if(cc(mid))l=mid; else r=mid; } //printf("%.2lf\n",r); 感受這裏應該有浮點偏差,不加floor函數的不到正確值 printf("%.2lf\n",floor(r*100)/100); //幸虧樣例給的好,否則WA了,徹底找不出來0.0; } int main() { scanf("%d%d",&n,&k); for(int i=0;i<n;i++) scanf("%lf",&L[i]); solve(); return 0; }
最大化平均值,01規劃,最大化最小值,最小化最大值。
例:http://codeforces.com/contest/626/problem/C
#include <bits/stdc++.h> using namespace std; //這二分用的太精緻了,妙呀QAQ; int n,m; bool check(int x)//二分的斷定基本都是用貪心去check; { int num1=x/2; int num2=x/3; int num3=x/6; if(num1<n)return false; if(num2<m)return false; if(min(num3,num1-n)<m-(num2-num3))return false;//判斷2,3的公倍數儘可能知足是否成立; else return true; } int main() { scanf("%d%d",&n,&m); int l=0,r=1e7,ans=0; while(l<=r){ int mid=(l+r)/2; if(check(mid))ans=mid,r=mid-1; else l=mid+1; } cout << ans << endl; return 0; }