二分内容整理(待补充

内容来算法竞赛进阶指南,我把自认为的重点写了下来,方便查看

二分

整数

对于单调递增序列a中查找>=x的数中最小的一个:

while(l<r)
{
    int mid=(l+r)>>1;
    if(a[mid]>=x) r=mid;
    else l=mid+1;
}
cout<<l<<endl;

对于单调递增序列a中查找<=x的数中最大的一个:

while(l<r)
{
    int mid=(l+r+1)>>1;
    if(a[mid]<=x) l=mid;
    else r=mid-1;
}
cout<<l<<endl;

r,l的取值简单的数学知识就能看懂。
关键是俩个写法的的mid的取值,如果对第二段代码也是(l+r)>>1,那么当r-l等于1时候,就有mid=(l+r)>>1=l。
若是进入l=mid,则死循环,若进入r=mid-1,则l>r,循环不能以l>r结束。(能用<<1别/2,因为右移是向下取整而除法是向零取整。可能会发生意想不到的错误)

步骤:

1.分析问题,左右区间那个是可行区间,以及mid归属哪一半段。

2.根据分析结果选择代码1或者代码2。

3.二分的中止条件即l==r,该值就是答案所在的位置。
bool check(int x) {/* ... */} // 检查x是否满足某种性质

二分判定问题

区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:

    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;    
        else l = mid + 1;
    }
    ans=l;

区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:

    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    ans=l;

实数

用eps

一般需要保留k位小数的时候,取eps=10^(-k+2。

   while(r-l>1e-5)
    {
        double mid=(1+r)/2;
        if(check(mid)) r=mid;
        else l=mid;
    }

采用固定循环

  for(int i=1;i<=100;i++)
    {
        double mid=(l+r)/2;
        if(check(mid))r=mid;
        else l=mid;
    }

固定循环得出的精度一般会比用eps高一点。

三分

对象,单峰函数(详情请回忆数学课本
对象单峰函数f,在定义域上[l,r]上,取lmid与rmid,将函数分为三段。
1.若f(lmid)<f(rmid), 则极大值在l和r之间或者在r的右侧(脑补一下函数图像),这种情况话,令l=lmid。
2.若f(lmid)>f(rmid),同上,令r=rmid。
和二分相同,也是在log级别求出极值。
ps:一定要注意题目给定的东西抽象出的函数是不是严格单调的。

原文地址:https://www.cnblogs.com/Aracne/p/12812269.html