二分搜法,是通过不断缩小解可能存在的范围,从而求得问题最优解的方法。
举个例子:
从有序数组中查找某个值:
lower_bound
给定长度是n的单调不下降序列,a[0],a[1],a[2],...........a[n-1],和一个数k,求满足a[i]>=k条件的最小的i。不存在的情况下输出n.
限制条件:
1<=n<=10^6
0<=k<=10^9
0<=a[0]<=a[1]<=.....a[n-1]<=10^9
输入:
n=5
a={2,3,3,5,6}
k=3
输出:
1(a[0]<3,a[1]>=3)
2 3 3 5 6
a[2]=3和3进行比较,可以知道解不大于2。
<span style="white-space:pre"> </span>2 3 3<span style="white-space:pre"> </span>5 6
a[1]=3和3进行比较,可以知道解不大于1。
<span style="white-space:pre"> </span>2 3 3 5 6
a[0]=2和3进行比较,可以知道解大于0。
2<span style="white-space:pre"> </span>3 3 5 6
解的变化范围
算法描述:
int n,k;
int a[max_n];
void solve()
{
//初始化解的存在范围
int low=-1;
int high=n;
//重复循环,直到解的存在范围不大于1
while(high-low>1)
{
int mid=(low+high)/2;
if(a[mid]>=k)
{
//如果mid满足条件,则解的存在范围变为(low,mid]
high=mid;
}
else
{
如果不满足条件,则解的存在范围变为(mid,high]
low=mid;
}
}
//这时,low+1=high
printf("%d\n",high);
}
这种算法就是二分搜法。
此外STL以lower_bound函数的形式实现了二分搜。这个算法处理在有序数组中查找值之外,在求最优解的问题上也非常有用。
让我们考虑一下“求满足某个条件C(x)的最小的x”这一个问题。对于任意满足C(x)的x,如果所有的x'>=x也满足C(x')的话,我们就可一使用二分搜来求得最小的x。
首先我们将区间的左端点初始化为不满足C(x)的值,右端点初始化为满足C(x)的值。然后每次去终点,mid=(low+high)/2,判断C(mid)是否满足并缩小范围,
直到(low,high]足够小了为止。最后high就是要求得最小值。求最大值用同样的方法。
二分搜索的阶数判定:
在输出小数的问题中,一般都会指定允许的误差范围,或者是指定输出中小数点后面的位数。因此在使用二分搜索时有必要设置合理的结束条件来满足精度的要求。
可以用循环次数最为终止条件。一次循环将范围缩小一半,循环100次则可以达到10的负30 次幂的精度范围,基本上是没有问题的。除此之外,也可以将终止条件设置为像
(high-low)>EPS这样,指定一个区间的大小。在这种情况下,如果EPS取得太小了,就有可能因为浮点小数精度的原因导致陷入死循环,千万小心。
总结一下二分搜索
最新推荐文章于 2020-11-12 20:03:57 发布