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; }