当前位置: 首页 > news >正文

二分查找专题(十三):“答案二分”的“三连击”——「制作m束花所需的最少天数」

哈喽各位,我是前端小L。

欢迎来到我们的二分查找专题第十三篇!“答案二分”的威力,在于它能“四两拨千斤”,将一个复杂的最优化问题,转化为一个简单的“Yes/No”判定问题。

今天,我们将用这个“大杀器”,去解决一个关于“等待”和“制作”的优美问题。我们要找的,不再是“最小速度”,也不是“最低运力”,而是“最少天数”。

力扣 1482. 制作 m 束花所需的最少天数

https://leetcode.cn/problems/minimum-number-of-days-to-make-m-bouquets/

题目分析:

  • 输入:花朵的开花日期数组 bloomDay,目标花束 m,每束花需要 k相邻的花。

  • 目标:找到一个最少的天数 d,使得在第 d 天时,我们可以从花园里采到 m 束花。

  • 规则:只有在 bloomDay[i] <= d 时,第 i 朵花才能被采摘。

核心洞察:d (天数) 的单调性 这个“最少天数 d”,就是我们要二分查找的“答案”。

  • 让我们来验证一下“单调性”。

  • 假设我们有一个 check(days) 函数,它能判断在 days 天后,能否制作 m 束花。

  • 如果 check(10) == true(第10天,能做够),那么在第 11 天(花开得更多了),也一定能做够 (true)。

  • 如果 check(10) == false(第10天,做不够),那么在第 9 天(花开得更少),一定也做不够 (false)。

这又是在“答案范围”(即天数范围)上,形成了一个我们无比熟悉的隐式数组: [F, F, F, ..., F, T, T, T, ...] 我们的目标,就是找到第一个 T!这又双叒是一个**lower_bound** 问题!

“答案二分”的建模

1. check(days) 函数的实现 (O(n)) 这是“答案二分”的“判定”核心。给定一个 days,能否制作 m 束花?

  • 我们可以用贪心策略来验证:

  • 初始化 bouquets_made = 0(已做花束),flowers_in_a_row = 0(当前连续开花的数量)。

  • 遍历 bloomDay 数组,索引为 i

    • if (bloomDay[i] <= days)

      • 太好了,这朵花开了!

      • flowers_in_a_row++

      • 检查能否做成一束花: if (flowers_in_a_row == k)

        • 成功!bouquets_made++

        • 重置连续计数:flowers_in_a_row = 0

    • else (bloomDay[i] > days):

      • 这朵花没开。

      • 连续性被打破

      • flowers_in_a_row = 0

  • 遍历结束后,返回 bouquets_made >= m

2. 搜索范围 [left, right) 的确定

  • 前置判断:如果需要的总花朵数 m * k 超过了数组长度 n,那么永远不可能做成,直接返回 -1。(这是一个高质量的“剪枝”)

  • left (最少可能天数):*min_element(bloomDay.begin(), bloomDay.end())

  • right (最大可能天数):*max_element(bloomDay.begin(), bloomDay.end())

  • 区间定义[left, right) -> [min_day, max_day + 1)

    • left = min_day

    • right = max_day + 1。(+1 是为了让 max_day 这个可能的答案,包含在我们的左闭右开区间内)。

3. 套用“万能模板”

  • left = min_day, right = max_day + 1

  • while (left < right)

  • mid = ...

  • if (check(mid)) (即 mid 天可行, T):

    • mid 可能是答案,但我们想找“最少”的,所以试试更小的。

    • 答案在左侧 [left, mid)

    • right = mid

  • else (!check(mid)) (即 mid 天不行, F):

    • mid 肯定不是答案,答案在右侧。

    • left = mid + 1

  • 最终答案left。(如果 left 超过了 max_day,说明无解,但我们的 check 函数会保证,如果 check(max_day) 都是 falseleft 会停在 max_day + 1。我们可以在最后判断一下,或者在 checktrue 分支里记录 ans

代码实现

#include <vector>
#include <numeric>
#include <algorithm> // for min_element, max_elementusing namespace std;class Solution {
private:// 判定函数:在 'days' 天内,能否制作 m 束花,每束 k 朵bool check(vector<int>& bloomDay, int m, int k, int days) {int bouquets_made = 0;int flowers_in_a_row = 0;for (int day : bloomDay) {if (day <= days) { // 花开了flowers_in_a_row++;if (flowers_in_a_row == k) {bouquets_made++;flowers_in_a_row = 0;}} else { // 花没开,打断了连续性flowers_in_a_row = 0;}}return bouquets_made >= m;}public:int minDays(vector<int>& bloomDay, int m, int k) {int n = bloomDay.size();// 剪枝:花的总数都不够if ((long long)m * k > n) {return -1;}// 1. 确定答案范围 [left, right)int left = *min_element(bloomDay.begin(), bloomDay.end());int right = *max_element(bloomDay.begin(), bloomDay.end()) + 1;int ans = -1; // 用于记录可行的答案// 2. 循环while (left < right) {// 3. midint mid = left + (right - left) / 2;// 4. 指针移动if (check(bloomDay, m, k, mid)) {// mid 天可行 (T),可能是答案,尝试更小的ans = mid; // 记录下这个可行的答案right = mid;} else {// mid 天不行 (F),必须等更久left = mid + 1;}}// 循环结束,left (或 right) 指向第一个 T// 或者,我们返回最后一次 check 为 true 时的 ans// 如果 check 从未为 true(比如 m*k > n),ans 会保持 -1// 但我们已经在开头剪枝了。// 如果 check(max_day) 都是 false, ans 保持 -1, left 会变成 max_day+1。// 但根据题意,如果可达,一定在 [min_day, max_day] 内。// 所以,循环结束时,left 就是最小天数。// (如果 check(max_day) 为 false, left 会=max_day+1, right 会=max_day+1)// (如果 check(min_day) 为 true, right 会=min_day, left 会=min_day)// 我们的模板,最终 left 就是答案// 思考:如果无解怎么办?比如 `m=1, k=1`, `bloomDay=[10]`. `left=10, right=11`. `mid=10`. `check(10)`=true. `ans=10, right=10`. `[10, 10)` 结束。返回 `left=10`。// 如果 `m=2, k=1`, `bloomDay=[10]`. `m*k > n` -> -1.// 如果 `m=1, k=2`, `bloomDay=[10]`. `m*k > n` -> -1.// 所以,只要过了剪枝,就一定有解return left;}
};

深度复杂度分析

  • check(days) 函数

    • 遍历 bloomDay 数组一次,O(n),其中 nbloomDay.size()

  • 二分查找

    • 搜索范围是 [min_day, max_day]。设 M = max(bloomDay)

    • 循环次数为 O(log M)。

  • 总时间复杂度

    • O(n * log M)

  • 空间复杂度 O(1)

    • 我们只使用了常数个额外变量。

总结

今天这道题,是“答案二分”模型的第三次精彩演绎。 我们已经形成了一个肌肉记忆般的“三部曲”:

  1. 识别模型:“最小化...天数”、“最小化...速度”、“最小化...运力” -> 答案二分。

  2. 分析单调性check(answer) 必须满足 [F...T] 结构。

  3. 构建 check 函数:用 O(n) 的贪心(或其他方法)来验证一个“猜测”的答案是否可行。

从“珂珂”、“运包裹”到“做花束”,问题的“外壳”在变,但“答案二分 + O(n)贪心验证”的算法内核,坚如磐石。

下期见!

http://www.dtcms.com/a/601277.html

相关文章:

  • 快3网站制作 优帮云简述网站建设的方法
  • Java1112 基类 c#vscode使用 程序结构
  • 第30节:大规模地形渲染与LOD技术
  • Goer-Docker系列1-容器数据持久化
  • 天机学堂——day1(修改bug)
  • 国外网站设计欣赏长沙网页设计哪家专业
  • php做图片交互网站代码成都制作网站公司
  • AI应用开发神器coze(扣子):使用智能体生成文案和图片
  • Java·如何区别多态中的“重写”与“重载”。
  • B端系统自动化MCP工具开发指南
  • 外贸整合营销网站如何快速开发手机app
  • 谢赛宁×李飞飞×LeCun联手重磅|Cambrian-S:「视频空间超感知」新范式,实现真正持续视频感知
  • 在服务器网站上做跳转网站运营推广方式
  • Ansible 安装与入门
  • VMMap 学习笔记(8.7):命令行模式与自动抓取——无界面采集内存证据的正确姿势
  • 大型网站服务器得多少钱app大全免费软件排行榜
  • AXI-5.3.2~5.3.5
  • Anaconda安装与配置:构建人工智能开发环境
  • 从入门到精通:周志华《机器学习》第一、二章深度解析
  • 网站建设品牌策划装修设计软件排名
  • 社区投稿 | Oinone应用于整车制造供应链决策
  • 加强网站建设的制度网站网址怎么找
  • 【Git】Git04:分支管理
  • R语言用什么编译器 | 如何选择最适合你的R语言开发环境
  • cuda12 cudnn9 tensorflow 显卡加速
  • 网站建设目标的文字嘉兴企业网站排名
  • 手机网站开发语言选择怎么能在网上卖货
  • 编程算法学习
  • 在Beego框架中创建Services层
  • PPT插入的音乐怎么让它播放到某一页就停?