关于二分的思考
二分的思想:假设数列是一堆有序的序列,题目要求我们求某个数是否在序列中,则每次把区间拆分成一半,在其中一半中寻找,遍历时间为O(logn)。
有时候,用二分的题目并非是单调递增的,则需要二重判断合法性,也就是说找到符合的数时,看看前面时候还有数也符合,如果还有则证明这个不是
最优解,继续追溯下去,如http://codeforces.com/problemset/problem/287/B
模板:
int a[555]; int bsearch(int key,int l,int r) { int mid; while(l<=r) { mid=(l+r)>>1; if(a[mid]==key) return mid; else if(a[mid]>key) r=mid-1; else l=mid+1; } }
二分变变变,返回最小符合元素的下标:
int bsearch(int x) { int low=0,high=n-1,mid; while(low<high) { mid=low+(high-low)/2; if(a[mid]>=x) high=mid;//如果a[mid]==x,继续找下去~,直到搜索空间变成0了 else low=mid+1; } if(a[low]==x) return low;//注意:假设一定存在,return的是low,不存在也很简单,if(a[low]!=x) return -1; return -1; }
void bsearch() { int low=0,high=maxn,mid; while(low<=high) { mid=(low+high)/2; if(greed(mid)) low=mid+1; else high=mid-1; } printf("%d\n",low-1); }
二分变变变,返回最大符合元素的下标:
/* 假如数列并非严格单调递增的(有多个相同值的元素), 那么此举会返回最大的符合要求的元素下标
测试序列:
8
1 2 4 4 4 4 5 5
貌似这种算法有bug?参照下一种 */ int bsearch(int x) { int low=0,high=n-1,mid; while(low<=high) { mid=(low+high)>>1; if(a[mid]<=x) low=mid+1;//如果a[mid]==x,继续找下去~,直到搜索空间变成0了 else high=mid-1; } return high;//注意:假设一定存在,return的是high }
binary_search(lo, hi, p): while lo < hi: mid = lo + (hi-lo+1)/2 // note: division truncates if p(mid) == true: hi = mid-1 else: lo = mid if p(lo) == true: complain // p(x) is true for all x in S! return lo // lo is the greatest x for which p(x) is false
You may also wonder as to why mid is calculated using mid = lo + (hi-lo)/2 instead of the usual mid = (lo+hi)/2. This is to avoid another potential rounding bug: in the first case, we want the division to always round down, towards the lower bound. But division truncates, so when lo+hi would be negative, it would start rounding towards the higher bound. Coding the calculation this way ensures that the number divided is always positive and hence always rounds as we want it to. Although the bug doesn't surface when the search space consists only of positive integers or real numbers, I've decided to code it this way throughout the article for consistency.
找实数:
binary_search(lo, hi, p): while we choose not to terminate: mid = lo + (hi-lo)/2 if p(mid) == true: hi = mid else: lo = mid return lo // lo is close to the border between no and yes
Since the set of real numbers is dense, it should be clear that we usually won't be able to find the exact target value. However, we can quickly find some x such that f(x) is within some tolerance of the border between no and yes. We have two ways of deciding when to terminate: terminate when the search space gets smaller than some predetermined bound (say 10-12) or do a fixed number of iterations.
二分法作为分治中最常见的方法,适用于单调函数,逼近求解某点的值。但当函数是凸性函数时,二分法就无法适用,这时三分法就可以“大显身手。
三分的模板:
double dis(double a)
{
//函数~
}
double tsearch(double l,double r) { double mid,midmid,mid_val,midmid_val; while(r-l>1e-6) { mid=(l+r)/2; midmid=(mid+r)/2; mid_val=dis(mid); midmid_val=dis(midmid); if(mid_val<=midmid_val) r=midmid; else l=mid; } return l; }
浙公网安备 33010602011771号