不光是查找值!“二分搜索”
二分搜索法:是通过不断缩小解可能存在的范围,从而求得问题最优解的方法。在程序设计竞赛或者刷题网站上,我们常常会见到,二分搜索法和其他算法结合的题目。
常见的二分搜索代码:
int MyBinarySearch(const vector<int> &ivec, int val){
if(ivec.size()==0) return -1;
auto beg = ivec.begin(),end = ivec.end()-1;
while(beg<=end){
auto mid =beg;
std::advance(mid,std::distance(beg,end));
if(*mid == val) return std::distance(ivec.begin(),mid);
else if(*mid > val) end = mid-1;
else if(*mid < val) beg = mid+1;
}
return -1;
}
二分查找易错点:链接
下面我们来看几道二分搜索法的问题。
1. 从有序数组中查找某个值( lower_bound )
题述:
给定长度为 n 的单调不下降数列 a0,------an-1 和一个数 k,求满足 ai>=k 条件的最小的 i。不存在的情况下输出 n。
限制条件:
- 1 <= n <= 10^6
- 0 <= a0<=a1-------<=an-1 <10^9
- 0 <= k <=10^9
题解:
- 我们要以 ( ] 半包围来求解 不小于的第一个元素问题
- 循环条件为l 和 r之间的位置大于1,这样保证 r 取到的右游标为准确的目标角标
- 如果中间值 大于等于 目标值,那么使用右 游标,缩小二分查找范围
- 中间值如果小于目标值,使用左游标,缩小二分查找范围,
int Mylower_bound(const vector<int>& ivec,const int &k){
int l=-1,r=ivec.size()-1;
while(r-l>1){
auto mid = (l+r)>>1;
if(ivec.at(mid) >= k) r=mid;
else l=mid;
}
return r;
}
int Myupper_bound(const vector<int>& ivec,const int &val){
int l=-1,r=ivec.size()-1;
while(r-l >1){
auto mid = (l+r)>>1;
if(ivec.at(mid) > val) r=mid;
else l = mid;
}
return r;
}
STL Lower_bound 代码:
template<typename _ForwardIterator, typename _Tp, typename _Compare>
_ForwardIterator
__lower_bound(_ForwardIterator __first, _ForwardIterator __last,
const _Tp& __val, _Compare __comp)
{
typedef typename iterator_traits<_ForwardIterator>::difference_type
_DistanceType;
_DistanceType __len = std::distance(__first, __last);
while (__len > 0)
{
_DistanceType __half = __len >> 1;
_ForwardIterator __middle = __first;
std::advance(__middle, __half);
if (__comp(__middle, __val))
{
__first = __middle;
++__first;
__len = __len - __half - 1;
}
else
__len = __half;
}
return __first;
}
2. 假定一个解并判断是否可行( Cable master POJ:No. 1064)
题述:有N条绳子 ,他们的长度分别为 Li 。如果从他们中切割出 K 条长度相同的绳子的话,每条绳子每条最长能有多长 ?答案保留到小数点后 2 位。
限制条件:
- 1 <= N <= 10000
- 1 <= K <= 10000
- 1 <= Li <= 100000
输入: N=4 K=11 L={8.02 , 7.43 , 4.57, 5.39}
输出: 2.00(每条绳子可以分成 4 条,3条,2条,2条)共计11条
题解: 我们套用二分搜索的模型得出问题。 令:
条件 C(x):= 可以得到 K 条长度为 x 的绳子
之后该问题就变成了求最大的 x 值的问题。
思路解释;:使用边界法表示 x 的边界,每一次通过判断 该 使用 x 求得的绳子个数缩进边界,大概循环100次就能达到想要的数值,之后取小数点后两位即可。
bool SatisfyAmount(double x,const vector<double>& dvec,const int desCount){
int amount = 0;
for (decltype(dvec.size()) i = 0; i !=dvec.size(); ++i) {
amount += (int)(dvec.at(i)/x);
}
return amount>=desCount;
}
double MaxInterceptLength(const vector<double>& dvec,const int desCount){
double l=0,r=INT64_MAX;
for (int i = 0; i !=100; ++i) {
double mid = l+(r-l)/2;
if(SatisfyAmount(mid,dvec,desCount)) l = mid;
else r = mid;
}
return floor(r*100)/100;
}
3. 最大化最小值 (Aggressive cows POJ No.2456)
题述: 农夫约翰搭了一间有 N 间牛舍的小屋。 牛舍排在一条线上, 第 i 号牛舍在 xi 的位置。 但是他的 M 头牛对小屋很不满意,因此互相攻击 。 约翰为了防止牛之间相互伤害, 因此把每头牛都放在离其他牛尽可能远的牛舍。 也就是要最大化最近的两头牛之间的距离。
样例:
输入 N=5,M=3,x={1,2,8,4,9}