内容来算法竞赛进阶指南,我把自认为的重点写了下来,方便查看
二分
整数
对于单调递增序列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:一定要注意题目给定的东西抽象出的函数是不是严格单调的。