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

2106. 摘水果

2106. 摘水果

初始理解问题

首先,我们需要明确题目要求:

  • 输入​:

    • fruits:一个列表,其中每个元素是一个包含两个整数的列表 [position, amount],表示在位置 position 上有 amount 个水果。
    • startPos:一个整数,表示起始位置。
    • k:一个整数,表示最多可以移动的步数。
  • 输出​:

    • 一个整数,表示在最多移动 k 步的情况下,能够收集到的最大水果数量。
  • 移动规则​:

    • 每次可以向左或向右移动一步(即位置 +1 或 -1)。
    • 移动的总步数不能超过 k
    • 可以收集经过的位置上的水果(假设收集水果不需要额外步数,或者是在移动过程中自动收集)。

解题思路

为了找到在最多 k 步内能收集到的最大水果数量,我们需要考虑所有可能的移动路径,这些路径的移动步数不超过 k,然后计算在这些路径上能收集到的水果总数。

直接枚举所有可能的路径显然不现实,因为步数可以很大。因此,我们需要一种更高效的方法。

关键观察
  1. 移动范围​:

    • 最远可以移动到 startPos - k 到 startPos + k 的位置。
    • 但是,由于可以向左或向右移动,实际能到达的最远位置可能更受限制。
  2. 移动策略​:

    • 可以看作是在一个区间内移动,这个区间由 startPos 和 k 决定。
    • 我们需要找到一个连续的区间 [L, R],使得从 startPos 出发,移动到这个区间的最左或最右,然后可能来回移动,总步数不超过 k
  3. 滑动窗口​:

    • 可以使用滑动窗口的方法来维护一个区间 [left, right],表示当前考虑的水果位置区间。
    • 通过调整 left 和 right 来确保移动步数不超过 k,并计算窗口内的水果总数。
具体步骤
  1. 预处理​:

    • 首先,将 fruits 按照位置排序(假设输入已经按位置排序,因为题目没有明确说明,但通常这类问题会保证输入有序)。
    • 使用 bisect 模块来快速找到起始位置附近的水果。
  2. 初始窗口​:

    • left 是第一个位置 >= startPos - k 的水果的索引。
    • right 是第一个位置 >= startPos + 1 的水果的索引(即位置 <= startPos 的最大索引 + 1)。
    • 初始时,计算从 left 到 right - 1 的水果总数,即从 startPos - k 到 startPos 的水果数。
  3. 滑动窗口扩展​:

    • 然后,逐步向右扩展 right,即考虑更远的位置。
    • 对于每个新的 right,将对应的水果数量加入当前总和 s
    • 然后,调整 left 以确保从 startPos 出发,移动到 fruits[right][0] 或 fruits[left][0] 的总步数不超过 k
      • 移动步数的计算:
        • 从 startPos 移动到 fruits[right][0] 然后可能来回移动。
        • 两个主要的移动模式:
          1. 先向右到 fruits[right][0],然后向左到 fruits[left][0] 再返回。
          2. 先向左到 fruits[left][0],然后向右到 fruits[right][0] 再返回。
        • 具体步数计算:
          • (先右后左)从 startPos 到 fruits[right][0] 然后到 fruits[left][0] 的步数为 (fruits[right][0] - startPos) + (fruits[right][0] - fruits[left][0]) = 2 * fruits[right][0] - startPos - fruits[left][0]
          • (先左后右)或者从 startPos 到 fruits[left][0] 然后到 fruits[right][0] 的步数为 (startPos - fruits[left][0]) + (fruits[right][0] - fruits[left][0]) = fruits[right][0] + startPos - 2 * fruits[left][0]
        • 简化后的条件:
          • fruits[right][0] * 2 - fruits[left][0] - startPos > k 或 fruits[right][0] - fruits[left][0] * 2 + startPos > k
      • 如果步数超过 k,则移动 left 向右,减少窗口内的水果数量。
  4. 更新最大值​:

    • 在每次调整 left 和 right 后,计算当前窗口内的水果总数 s,并更新最大值 ans

代码解释

class Solution:def maxTotalFruits(self, fruits: List[List[int]], startPos: int, k: int) -> int:left = bisect_left(fruits, [startPos - k])  # 向左最远能到 fruits[left][0]right = bisect_left(fruits, [startPos + 1])  # 位置 <= startPos 的最大下标 + 1ans = s = sum(f[1] for f in fruits[left: right])  # 从 fruits[left][0] 到 startPos 的水果数# 枚举最右走到 fruits[right][0]while right < len(fruits) and fruits[right][0] <= startPos + k:s += fruits[right][1]while left < right and (fruits[right][0] * 2 - fruits[left][0] - startPos > k or fruits[right][0] - fruits[left][0] * 2 + startPos > k):s -= fruits[left][1]  # fruits[left][0] 太远了left += 1ans = max(ans, s)  # 更新答案最大值right += 1  # 继续枚举下一个最右位置return ans
  1. 初始 left 和 right 的确定​:

    • left = bisect_left(fruits, [startPos - k])
      • 使用二分查找找到第一个位置 >= startPos - k 的水果的索引。这是向左最远可以到达的位置。
    • right = bisect_left(fruits, [startPos + 1])
      • 使用二分查找找到第一个位置 >= startPos + 1 的水果的索引。这相当于位置 <= startPos 的最大索引 + 1。
  2. ​**初始水果总数 ans 和 s**​:

    • ans = s = sum(f[1] for f in fruits[left: right])
      • 计算从 left 到 right - 1 的水果数量总和,即从 startPos - k 到 startPos 的水果数。这是初始窗口内的水果总数。
  3. 滑动窗口扩展​:

    • while right < len(fruits) and fruits[right][0] <= startPos + k:
      • 只要 right 没有超出 fruits 的范围,并且当前 fruits[right][0] 不超过 startPos + k,就继续扩展。
    • s += fruits[right][1]
      • 将当前 right 位置的水果数量加入总和 s
    • ​**调整 left**​:
      • while left < right and (条件)
        • 当 left 没有超过 right,并且移动步数超过 k 时,移动 left 向右。
        • 条件:
          • fruits[right][0] * 2 - fruits[left][0] - startPos > k
            • 表示从 startPos 到 fruits[right][0] 然后到 fruits[left][0] 的步数超过 k
          • fruits[right][0] - fruits[left][0] * 2 + startPos > k
            • 表示从 startPos 到 fruits[left][0] 然后到 fruits[right][0] 的步数超过 k
        • 如果满足任一条件,则 s -= fruits[left][1] 并 left += 1,即减少窗口左边界的水果数量。
    • 更新最大值​:
      • ans = max(ans, s)
        • 每次调整后,计算当前窗口内的水果总数 s,并更新最大值 ans
    • right += 1
      • 继续向右扩展窗口。
  4. 返回结果​:

    • return ans
      • 返回能够收集到的最大水果数量。

为什么这样有效

  • 滑动窗口​:通过维护一个窗口 [left, right],确保窗口内的水果可以通过不超过 k 步的移动收集到。
  • 步数计算​:通过两个不等式确保从 startPos 出发,移动到窗口的最左或最右位置的总步数不超过 k
  • 高效性​:利用二分查找快速定位初始窗口,并通过滑动窗口线性遍历可能的区间,保证了算法的高效性。

可能的疑问

  1. ​**为什么 right 初始是 bisect_left(fruits, [startPos + 1])**​:

    • 这是为了找到位置 <= startPos 的最大索引 + 1。即 right 初始指向第一个位置 > startPos 的水果,因此 fruits[left: right] 包含位置 <= startPos 的水果。
  2. 步数计算的两个条件​:

    • 这两个条件分别对应两种移动策略:
      • 先向右到最远,然后向左。
      • 先向左到最远,然后向右。
    • 通过这两个条件可以确保总步数不超过 k

总结

该解决方案通过滑动窗口和二分查找高效地找到了在最多 k 步内能收集到的最大水果数量。关键在于如何通过两个步数条件来调整窗口的左右边界,确保移动步数的限制被满足。

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

相关文章:

  • 数据结构中使用到的C语言
  • RocksDb 是什么?levelDB、LSM 树、SSTable又分别是什么?区别呢?
  • Linux 内存调优之如何限制进程、系统级别内存资源
  • 第二章 矩阵
  • 剥离petalinux设备树,使用 dtc 单独编译
  • 主流身份认证协议都有哪些?应用场景有何区别?
  • BRL贝叶斯规则列表
  • 《C++》stack容器详解
  • 块三角掩码(Block-Triangular Masking)
  • Remix框架:高性能React全栈开发实战
  • 安卓加固脱壳
  • js--2048小游戏
  • C++23 Concepts:用类型约束重构泛型编程的终极方案
  • 构造类型--结构体,共同体联合体,枚举
  • 【AI论文】Rep-MTL:释放表征级任务显著性在多任务学习中的潜力
  • 影响人类发音的疾病类型种类和数据集
  • CMake 命令行参数完全指南(2)
  • 界面规范4-按钮
  • All the Mods 9 - To the Sky - atm9sky 局域网联机报错可能解决方法
  • spring batch处理数据模板(Reader-Processor-Writer模式)
  • 【Mysql】日志--错误日志、二进制日志、查询日志、慢查询日志
  • Timer实现定时调度的原理是什么?
  • Python开发环境PyCharm下载与安装
  • RSA 解密逻辑
  • Spring lookup-method实现原理深度解析
  • 悬挂的绳子,它的函数方程是什么样子的?
  • 嵌入式学习日志——数据结构(一)
  • RAG与智能体技术全景解析:架构革新、场景落地与未来趋势
  • 【前端:Html】--1.2.基础语法
  • Redis面试精讲 Day 10:Redis数据结构底层实现原理