一文搞定 60 道 hard/meidum 算法题 - 二分法(二)

前言

在我看来,大部分面试的算法题从来都不是难在思维,而是缺乏系统的教学。它不像数学属于普及的基础教育,算法题目的大部分知识、技巧往往都局限于 competitive programming 当中 (比如各种 OI 竞赛、 ACM 竞赛等),这些都是大部分计算机行业从业者接触不到的。它就像一个大群体中一个半封闭的小群体一样,系统的知识就在那里,只是我们很少会主动走进去。因此,我期望将这些知识给带出来,就引申出了本系列文章和视频。

在 《二分法(一)》当中我们已经分别讲解了: - 整数域二分的两种模板 - 实数域二分的模板 - 二分算法的一种实际应用

那么,本篇的主要内容即是对剩下的四种应用进行深入分析。 1. 对结果进行二分(值域上二分) 2. min(max()) 和 max(min()) 3. 最大/最小平均值(max/min average) 4. 特殊第 K 小问题

注意:搭配 B 站视频更香奥!

正文

类型二:对结果进行二分

这类问题仍然属于最优化问题,该问题的所有方案组成问题的 值域,需要求所有方案中的最优方案。

而上述通过二分法能够处理的最优化问题需要满足:值域 满足某种特殊的单调性。用更加详细的话讲,就是能够通过题目特性将值域分成两个区间,一个区间满足特性,另外一个区间不满足特性 $$ f(x) = \begin{cases} true, x - 属于区间1 \ false,x - 属于区间2 \end{cases} $$

我们来看具体的例子: leetcode: 1011. 在 D 天内送达包裹的能力

(图 5.1)

题目需要求 D 天内能够传送所有包裹的最低运载能力,假设这个 最低运载能力x。那么很显然 x+1 也能够在 D 天内运完所有包裹。因此本题的 check 特性 就是 能在 D 天内运完所有包裹。我们可以根据该特性将值域分为两个区间:

(图 5.2)

那么值域的范围是多少呢? 左边界肯定不能 $\leq 0$; 右边界最快可以一次运完所有的包裹,也就是 $sum(weights)$。所以值域是 $[0, sum(weights)]$。

这里值得注意的是,有些题目不会给出右边界,因此我们需要根据经验写出右边界,比如 $10^{18}$ 等

分析到这里,算法思路就结束了。最后结果 $x$ 是 右区间的左端点,可以通过模板一来写。而 check() 方法可以很容易根据定义用贪心写出来。 ```python def check(x): cnt = 0 # 统计传输天数 curr = 0 # 当前的重量,不能超过 x

for t in weights:
    if t > x: return False # 如果单个物品超过 x, 很明显表示该运载能力不够

    curr += t  # 加上当前物品,看看是否会超重,超重的部分下一次再运
    if curr == x:
        cnt += 1
        curr = 0
    elif curr > x:
        cnt += 1
        curr = t

if curr > 0: cnt += 1 # 最后还剩余 curr(不超过运载能力),只能放在下一次再运 
return cnt <= D # 根据判断总运输天数是否超过 D,来判断该 x 能够满足要求

```

本算法的时间复杂度为 $O(nlog(ans))$, 而 $log(ans)$ 一般都不会很大(比如 $log(10^{10}) \approx 30$)。

有同学可能会问,该题的暴力朴素解法是什么? - 其实也很简单,组合数学。将数组切分成 D 个区间,不同的切分方式为不同的方案。最优方案是 $min(max(切分区间))$,复杂度为 $C(n-1, D)$, 阶乘级别。

而我们根据题目中隐含的单调性将复杂度极大地降低,下面我们来看看同类型的其他题目,首先给出个定义。

在值域上二分的这类题目,其代码区别主要在 check 方法,不同的题目会结合其他算法来进行考察。 比如 leetcode: 778. 水位上升的泳池中游泳

(图 5-3)

本题看似是个 hard 级别难度,如果你理解上上述的思路后,本题就变成了一个 easy 级别的。

同样来分析题目,假设最少耗时为 t, 很明显

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值