二分法

二分法

每次将搜索范围缩小一半,可以在O(log n)的时间找到正确答案。

二分法的前提条件:单调性

核心:利用单调性调整二分查找的区间

二分法步骤:
1、候选区间[left,right]
2、不断循环,直至区间满足特定条件
—2.1、计算中心点mid = (left + right) / 2
—2.2、判断中点是否合法,根据中点的计算结果调整[left,right]

二分法用途:
1、查找有序列表a中元素x的下标
2、浮点二分
3、二分答案

二分法题型

浮点二分

浮点二分

l , r = 1 , 2
eps = 1e-4

#当区间长度大于eps,说明前3位小数不同
#否则,前三位小数已确定
while r - l >= eps:
    print("[{},{}]".format(l,r))
    mid = (l + r) / 2
    #若mid²>2下次为[l,mid],否则[mid,r]
    if mid ** 2 > 2:
        r = mid
    else:
        l = mid

运行结果

[1,2]
[1,1.5]
[1.25,1.5]
[1.375,1.5]
[1.375,1.4375]
[1.40625,1.4375]
[1.40625,1.421875]
[1.4140625,1.421875]
[1.4140625,1.41796875]
[1.4140625,1.416015625]
[1.4140625,1.4150390625]
[1.4140625,1.41455078125]
[1.4140625,1.414306640625]
[1.4141845703125,1.414306640625]

二分答案

描述

题目所求答案(一般为整数)具有单调性质,采用猜答案+二分
1、确定初始范围[left,right]
2、当left<=right时:

  • mid = (left+right)//2
  • check(mid),判断mid是否合法:定义check函数,什么时候合法根据题目单调性来确定,如果合法,更新ans = mid
  • 根据合法调整左右区间:如left = mid+1 或right = mid-1

二分答案模板

#二分答案
def check(x):#判断是否合法,合法返回True,否则False
    pass
l , r , ans = 0#初始化
while l <= r:
    mid = (l + r) // 2 
    if check(mid):
        ans = mid
        l = mid + 1
    else:
        r = mid -1
print(ans)

99分巧克力

题目链接:99分巧克力
在这里插入图片描述
假设边长是x,初始区间[1,100000]
什么是合法?边长为x时,能否切出k块
如果mid合法,要求最大值,下一次为[mid+1,right],否则下次为[left,mid-1]

n , k = map(int,input().split())
a = []
for i in range(n):
    x , y = map(int,input().split())
    a.append((x,y))
def check(x):
    cnt = 0
    for h ,w in a :
        cnt += (h//x)*(w//x)
    return cnt>=k

l , r = 1 , 100000
ans = 1
while l <= r:
    mid = (l + r) // 2
    if check(mid):
        ans = mid
        l = mid +1
    else:
        r = mid -1
print(ans)

运行结果

2 10
6 5
5 6

2

364跳石头

题目链接:364跳石头
在这里插入图片描述
“最大值最小化”、“最小值最大化”,一般用二分答案
假设“最短跳跃距离”为x:

  • 是否有单调性
  • 什么叫做合法
  • 合法后如何调整区间
L, N, M = map(int, input().split())  # 输入长度为L的直线,岩石数量为N,最多移除数量为M
D = []  # 存储岩石的位置
for i in range(N):
    D.append(int(input()))  # 输入每个岩石的位置


def check(x):  # 判断当最短跳跃距离为x时,移走的岩石数量是否不超过M个
    now_idx = 0  # 当前坐标初始化为0
    cnt = 0  # 移除数量初始化为0
    for i in range(N):  # 遍历岩石位置
        if D[i] - now_idx < x:  # 如果当前岩石与前一个岩石之间的距离小于x,则需要移除这块岩石
            cnt += 1  # 移除数量加1
        else:  # 否则,更新当前坐标now_idx为当前岩石的位置
            now_idx = D[i]
    if L - now_idx < x:  # 判断最后一个岩石跳到终点的距离是否小于x
        return False  # 如果是,则返回False
    return cnt <= M  # 如果移除数量cnt小于等于M,则返回True,否则返回False


left, right, ans = 1, L, 1  # 二分查找区间为[1, L],初始答案为1
while left <= right:  # 当左边界小于等于右边界时,继续查找
    mid = (left + right) // 2  # 计算中间值mid
    if check(mid):  # 如果当最短跳跃距离为mid时,移走的岩石数量不超过M个
        ans = mid  # 更新答案为mid
        left = mid + 1  # 将左边界更新为mid+1
    else:
        right = mid - 1  # 否则,将右边界更新为mid-1
print(ans)  # 输出最短跳跃距离x的最大值

运行答案

25 5 2
2
11
14
17
21

4

3404肖恩的乘法表

题目链接:3404肖恩的乘法表
在这里插入图片描述

n, m, k = map(int, input().split())  # 输入三个整数 n、m、k
left, right = 1, n * m  # 初始化左右边界
 
def check(x):  # 定义一个函数,用于计算小于等于 x 的元素个数
    cnt = 0
    for i in range(1, n + 1):  # 遍历行数
        cnt += min(m, x // i)  # 计算当前行中小于等于 x 的元素个数,并累加到 cnt 中
    return cnt
 
ans = 0  # 初始化答案为 0
while left <= right:  # 二分查找
    mid = (left + right) // 2  # 计算中间值
    if check(mid) >= k:  # 如果小于等于 mid 的元素个数大于等于 k
        ans = mid  # 更新答案
        right = mid - 1  # 缩小右边界
    else:
        left = mid + 1  # 扩大左边界
print(ans) 

运行结果

2 4 5

4
  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

唐丶晚笙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值