二分算法

二分算法

实数二分不需要考虑是否补 1 的问题,因此相对而言更简单。

有单调性则一定可以二分,能二分却不一定具有单调性。没有单调性也可能可以二分。

整数二分思想

整数二分特别容易出现死循环。

  1. 确定一个区间,使得目标值(答案)一定在区间中。

  2. 找一个性质,这个性质具有二段性(95% 的二分都具有二段性):所谓的二段性就是区间内每个点都可以判断是否具有这个性质,并且利用这个性质,可以将一个区间划分为连续的两个部分,前半段区间满足性质,后半段不满足此性质,中间没有缺失的地方。 答案必须是二分的分界点。

    image-20200918151540792

  3. 分析中点 M 在该判断条件下是否成立,如果成立,考虑答案在哪个区间;如果不成立,考虑答案在哪个区间。(二分可以分为两大类,ans 是红色区域的右端点、ans 是绿色区域的左端点。)

  4. 如果更新方式写的是 R = M,则不用做任何特殊处理;如果更新方式写的是 L = M,则需要在计算 M 时额外加上 1,产生向上取整的效果,防止死循环。

以下模板基本可以解决所有二分问题。

第一类:ans 是红色区域的右端点

将 [L, R] 区间利用二段性分成 **[L, M - 1] 、 [M, R] **

if M 是红色,说明 ans 必然在 [M, R] 中:image-20200918152027938

else M 是绿色,说明 ans 必然在 [L, M - 1] 区间中。

根据上面的思想,有整数二分的第一个模板

int L = ,
    R = ; // 确定 [L,R]区间
while(L < R) {
    M = (L + R + 1) / 2; // 这里一定要额外+1,表示上取整。不补1,则为下取证,容易发生死循环
    if M = 红 L = M; // [M, R]
    else R = M - 1; // [L, M - 1]
}

记忆的小技巧:只要看 if 的判断条件是否有 L = M,如果有,则必须补上 + 1。

关于上述代码中的 M = (L + R + 1) / 2 说明:

一定要补上 1。

如果不补上 1 时,当 L = R - 1 时,即 R = L + 1,则 M = (L + R)/2 = (2*L + 1)/ 2 = L (C++ 默认下取整)。根据上述代码的步骤,当 M 在红色区域时,L = M = L,说明一次循环之后,L、M 都没有发生变化,这就导致了死循环。

如果补上 1 时,当 L = R - 1 时,即 R = L + 1,则 M = (L + R + 1) / 2 = (2*L + 2) / 2 = L + 1。根据上述代码的步骤,当 M 在红色区域时,L = M = L + 1,说明一次循环之后,L 发生了变化,这就不会发生死循环了。

C++ 上、下取整

C++ 默认是下取整,所以 (L + R) / 2 是 下取整,但是 (L + R + 1) / 2 的 下取整 就相当于 (L + R) / 2 的上取整 版本。

第二类:ans 是绿色区间的左端点

将 [L, R] 分成 [L, M][M + 1, R]

if M 是绿色,说明 ans 必然在 [L, M] 中

else 说明 ans 必然在 [M + 1, R] 中
整数二分的第二个模板

int L = ,
    R = ; // 确定 [L,R]区间
while (L < R) {
	M = (L + R) / 2;
    if M = 绿色 R = M;
    else L = M + 1;
}

只需要关注是否写了 R = M,如果写了,则一定不需要补 1。

关于上面代码为 M = (L + R) / 2 的说明

这次不会发生死循环。

当 L = R - 1 时,即 R = L + 1,M = (L + R) / 2 = (2*L + 1)/2 = L。此时,当 M 在 绿色区间时,R = L,说明一次循环 R 发生了变化,不会发生死循环。当 M 不在绿色区间时,L = M + 1 = L + 1,说明一次循环 L 发生了变化,不会发生死循环。

实数二分

实数二分有一个特点就是我们必然可以取到区间的中点,因为实数是稠密的。将区间划分成 [L, M][M, R]

因为实数二分是稠密的,因此 while 循环的结束条件不能是 L < R,应该结束条件定义成区间长度小于某个值,一般取 1e-6 即可。但是,如果题目中的小数保留了 x 位,那么我们一般取 1e-(x+2) 区间长度才可。

if ans 在 [M, R] L = M

else if ans 在 [L, M] R = M

while(R - L > 1e-6) {
    double M = (L + R) / 2;
    if(...) L = M;
    else R = M;
}

参考视频:ACWing-2.1二分与前缀和

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值