2106. 摘水果,梳理思路

文章目录

    • 题目概要
    • java 解法
    • 详解

在这里插入图片描述

题目概要

在一个无限的 x 坐标轴上,有许多水果分布在其中某些位置。给你一个二维整数数组 fruits ,其中 fruits[i] = [positioni, amounti] 表示共有 amounti 个水果放置在 positioni 上。fruits 已经按 positioni 升序排列 ,每个 positioni 互不相同 。

另给你两个整数 startPos 和 k 。最初,你位于 startPos 。从任何位置,你可以选择 向左或者向右 走。在 x 轴上每移动 一个单位 ,就记作 一步 。你总共可以走 最多 k 步。你每达到一个位置,都会摘掉全部的水果,水果也将从该位置消失(不会再生)。

返回你可以摘到水果的 最大总数 。

示例 1:
在这里插入图片描述

输入:fruits = [[2,8],[6,3],[8,6]], startPos = 5, k = 4
输出:9
解释:
最佳路线为:

  • 向右移动到位置 6 ,摘到 3 个水果
  • 向右移动到位置 8 ,摘到 6 个水果
    移动 3 步,共摘到 3 + 6 = 9 个水果

示例 2:
在这里插入图片描述

输入:fruits = [[0,9],[4,1],[5,7],[6,2],[7,4],[10,9]], startPos = 5, k = 4
输出:14
解释:
可以移动最多 k = 4 步,所以无法到达位置 0 和位置 10 。
最佳路线为:

  • 在初始位置 5 ,摘到 7 个水果
  • 向左移动到位置 4 ,摘到 1 个水果
  • 向右移动到位置 6 ,摘到 2 个水果
  • 向右移动到位置 7 ,摘到 4 个水果
    移动 1 + 3 = 4 步,共摘到 7 + 1 + 2 + 4 = 14 个水果

示例 3:

在这里插入图片描述

输入:fruits = [[0,3],[6,4],[8,5]], startPos = 3, k = 2
输出:0
解释:
最多可以移动 k = 2 步,无法到达任一有水果的地方

提示:
1<=fruits.length<=105fruits[i].length==20<=startPos,positioni<=2∗105对于任意i>0,positioni−1<positioni均成立(下标从0开始计数)1<=amounti<=1040<=k<=2∗1051 <= fruits.length <= 10^5 \\ fruits[i].length == 2\\ 0 <= startPos, positioni <= 2 * 10^5 \\ 对于任意 i > 0 ,positioni-1 < positioni 均成立(下标从 0 开始计数)\\ 1 <= amounti <= 10^4 \\ 0 <= k <= 2 * 10^5 1<=fruits.length<=105fruits[i].length==20<=startPos,positioni<=2105对于任意i>0positioni1<positioni均成立(下标从0开始计数)1<=amounti<=1040<=k<=2105

java 解法

public int maxTotalFruits(int[][] fruits, int startPos, int k) {int n = fruits.length;// 提取位置和数量,方便操作int[] positions = new int[n];int[] amounts = new int[n];for (int i = 0; i < n; i++) {positions[i] = fruits[i][0];  // 位置amounts[i] = fruits[i][1];    // 水果数量}// 前缀和数组:prefix[i] 表示前 i 个位置的水果总数int[] prefix = new int[n + 1];for (int i = 0; i < n; i++) {prefix[i + 1] = prefix[i] + amounts[i];}int maxFruits = 0;  // 记录能摘到的最大水果数int left = 0;       // 滑动窗口的左指针// 滑动窗口:右指针 right 从 0 到 n-1for (int right = 0; right < n; right++) {// 当前尝试的区间是 [left, right]int L = positions[left];      // 区间最左位置int R = positions[right];     // 区间最右位置// 计算从 startPos 出发,走完 [L, R] 所需的最少步数int steps = calculateSteps(startPos, L, R);// 如果步数超过了 k,说明走太远了,需要缩小窗口(移动 left)while (left <= right && steps > k) {left++;  // 缩小左边界if (left > right) break;L = positions[left];R = positions[right];steps = calculateSteps(startPos, L, R);}// 如果当前窗口有效,更新最大水果数if (left <= right) {int total = prefix[right + 1] - prefix[left];  // [left, right] 的水果总数maxFruits = Math.max(maxFruits, total);}}return maxFruits;
}// 计算从 startPos 出发,走完区间 [L, R] 所需的最少步数
private int calculateSteps(int startPos, int L, int R) {if (R <= startPos) {// 所有水果都在起点或左边:只向左走return startPos - L;} else if (L >= startPos) {// 所有水果都在起点或右边:只向右走return R - startPos;} else {// 水果分布在起点两侧// 方案1:先向左走到 L,再向右走到 Rint goLeftFirst = (startPos - L) * 2 + (R - startPos);// 方案2:先向右走到 R,再向左走到 Lint goRightFirst = (R - startPos) * 2 + (startPos - L);// 返回两种方案中步数更少的那个return Math.min(goLeftFirst, goRightFirst);}
}public static void main(String[] args) {Solution1 solution = new Solution1();// 示例 1int[][] fruits1 = {{2,8},{6,3},{8,6}};System.out.println(solution.maxTotalFruits(fruits1, 5, 4)); // 输出: 9// 示例 2int[][] fruits2 = {{0,9},{4,1},{5,7},{6,2},{7,4},{10,9}};System.out.println(solution.maxTotalFruits(fruits2, 5, 4)); // 输出: 14// 示例 3int[][] fruits3 = {{0,3},{6,4},{8,5}};System.out.println(solution.maxTotalFruits(fruits3, 3, 2)); // 输出: 0
}

详解

目的:将二维信息解耦为两个一维数组,便于后续使用双指针、二分查找等操作。

int n = fruits.length;// 提取位置和数量,方便操作
int[] positions = new int[n];
int[] amounts = new int[n];
for (int i = 0; i < n; i++) {positions[i] = fruits[i][0];  // 位置amounts[i] = fruits[i][1];    // 水果数量
}

前缀和(Prefix Sum)用来快速计算总和的一种方式
第 i+1 个累计值 = 第 i 个累计值 + 第 i 个位置的水果数

// 前缀和数组:prefix[i] 表示前 i 个位置的水果总数
int[] prefix = new int[n + 1];
for (int i = 0; i < n; i++) {prefix[i + 1] = prefix[i] + amounts[i];
}

拿示例 1: 举例
fruits = [[2,8], [6,3], [8,6]]
最终前缀和数组

prefix[i]含义对应的水果
prefix[0] = 0前 0 个位置的水果总数什么都没有
prefix[1] = 8前 1 个位置的水果总数位置 2:8 个
prefix[2] = 11前 2 个位置的水果总数位置 2+6:8+3=11
prefix[3] = 17前 3 个位置的水果总数位置 2+6+8:8+3+6=17

用前缀和快速算“某段区间的水果总数
🌰 例子 1:只摘位置 6 的水果(第 1 个位置)

  • left = 1, right = 1
  • 总数 = prefix[right+1] - prefix[left] = prefix[2] - prefix[1] = 11 - 8 = 3 ✅

🌰 例子 2:摘位置 6 和 8 的水果(第 1 和第 2 个位置)

  • left = 1, right = 2
  • 总数 = prefix[3] - prefix[1] = 17 - 8 = 9 ✅
    也就是 3 + 6 = 9

🌰 例子 3:摘所有水果(第 0 到第 2 个位置)

  • left = 0, right = 2
  • 总数 = prefix[3] - prefix[0] = 17 - 0 = 17 ✅

🌰 例子 4:只摘位置 2 的水果(第 0 个位置)

  • left = 0, right = 0
  • 总数 = prefix[1] - prefix[0] = 8 - 0 = 8
    这在滑动窗口中特别有用,因为 right 每移动一次,都要算一次区间和。

从左向右,滑动窗口
我们要在最多走 k 步的前提下,从起点 startPos 出发,摘到最多的水果。
maxFruits:就像一个“最高分记录”,通过 在 合理的 k 步条件下获取的最多水果数
外层循环:右指针 right 不断向右扩展
right 从 0 开始,一步步向右移动(0 → 1 → 2 → …)
每次 right 向右一格,就相当于我们想多摘一个位置的水果

for (int right = 0; right < n; right++) {

当前尝试的区间 [left, right]
拿示例 1: 举例
fruits = [[2,8], [6,3], [8,6]]
positions = [2, 6, 8]
那么 以下就会从

rightleft区间步数是否可达水果数maxFruits
00[2]388
10[2,6]5❌ → left=1--
1[6]13max(8,3)=8
21[6,8]39max(8,9)=9
int L = positions[left];      // 区间最左位置
int R = positions[right];     // 区间最右位置

计算区间区间步数
计算走完这个区间需要多少步
left <= right 缩小空间,保持空间合法性
steps > k 步数超过k步后,进入 while 调整左侧空间,使步数减少,
while (left <= right && steps > k)while 发现 septs 直到 空间合法后跳出循环

// 计算从 startPos 出发,走完 [L, R] 所需的最少步数
int steps = calculateSteps(startPos, L, R);// 如果步数超过了 k,说明走太远了,需要缩小窗口(移动 left)
while (left <= right && steps > k) {left++;  // 缩小左边界if (left > right) break;L = positions[left];R = positions[right];steps = calculateSteps(startPos, L, R);
}

校验合法性,并更新水果数
prefix[right + 1] - prefix[left] 这边用到了上面 所写的 前缀和 计算累计数量, left 跟 right 是 对应的坐标点,由于前缀和 第一个坐标点为 0 所以需要加1

prefix[i]含义对应的水果
prefix[0] = 0前 0 个位置的水果总数什么都没有
prefix[1] = 8前 1 个位置的水果总数位置 2:8 个
prefix[2] = 11前 2 个位置的水果总数位置 2+6:8+3=11
prefix[3] = 17前 3 个位置的水果总数位置 2+6+8:8+3+6=17

right + 1 前 right+1 个数的总和
left 前 left 个数的总和

amounts:     [ 8 ,  3 ,  6 ]↑    ↑    ↑
索引:         0    1    2prefix:  [0,  8,  11,  17]↑   ↑   ↑    ↑0   1   2    3

继续用示例1
fruits = [[2,8],[6,3],[8,6]]
比如 :获取 位置为6 的 水果, 由于 前缀和的缘故 第一个是固定值,后面的值是累加过来的
prefix[right + 1] - prefix[left] = prefix[2] - prefix[1] = 11 - 8 = 3
Math.max 对比 两数大小

// 如果当前窗口有效,更新最大水果数
if (left <= right) {int total = prefix[right + 1] - prefix[left];  // [left, right] 的水果总数maxFruits = Math.max(maxFruits, total);
}

计算步数
R <= startPos 从步数左侧走完的步数, 上面 for 循环 R 是在一步步从左走到右侧的
L >= startPos 从右侧走完的步数,L 因为上面空间缩小调整一步步走到右侧
获取 在 R 大于起始步数并 L 还没移动到右侧时 获取 中间的两种状态
(startPos - L) * 2 + (R - startPos) 先向左走到 L,再向右走到 R
(R - startPos) * 2 + (startPos - L) 先先向右走到 R,再向左走到 L
Math.min 获取两者最小步数方案,可能起始位置并不居中,导致 往回走步数减少减少的情况

// 计算从 startPos 出发,走完区间 [L, R] 所需的最少步数
private int calculateSteps(int startPos, int L, int R) {if (R <= startPos) {// 所有水果都在起点或左边:只向左走return startPos - L;} else if (L >= startPos) {// 所有水果都在起点或右边:只向右走return R - startPos;} else {// 水果分布在起点两侧// 方案1:先向左走到 L,再向右走到 Rint goLeftFirst = (startPos - L) * 2 + (R - startPos);// 方案2:先向右走到 R,再向左走到 Lint goRightFirst = (R - startPos) * 2 + (startPos - L);// 返回两种方案中步数更少的那个return Math.min(goLeftFirst, goRightFirst);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/91720.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/91720.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入理解消息队列(MQ)核心原理与设计精髓

引言&#xff1a;从一个“不堪重负”的订单系统说起想象一个简化的电商下单流程&#xff1a;用户点击“下单”后&#xff0c;系统需要&#xff1a;在订单数据库中创建一条记录。调用库存服务&#xff0c;扣减商品库存。调用营销服务&#xff0c;给用户发放积分和优惠券。调用通…

前端手撕题总结篇(算法篇——来自Leetcode牛客)

链表指定区域反转 找到区间&#xff08;头和为 for循环当**时&#xff09;->反转链表&#xff08;返回反转过后的头和尾&#xff09;->连接 function reverseBetween( head , m , n ) {//preEnd&cur&nextStart cur.next断开if(mn)return head;const vHeadNode…

从Excel到工时管理系统:企业如何选择更高效的工时记录工具?

还在为手工统计员工工时而头疼吗&#xff1f;月末堆积如山的Excel表格、反复核对的数据、层出不穷的差错&#xff0c;这些问题正在拖慢企业的发展步伐。8Manage工时管理系统发现&#xff0c;传统手工记录不仅耗费大量人力&#xff0c;更让宝贵的工时数据难以转化为有效的管理决…

Java设计模式之《命令模式》

目录 1、介绍 1.1、命令模式定义 1.2、对比 1.3、典型应用场景 2、命令模式的结构 2.1、组成部分&#xff1a; 2.2、整体流程 3、实现 3.1、没有命令模式 3.2、命令模式写法 4、命令模式的优缺点 前言 java设计模式分类&#xff1a; 1、介绍 1.1、命令模式定义 命…

【动态规划算法】路径问题

什么是动态规划算法动态规划&#xff08;Dynamic Programming&#xff0c;简称 DP&#xff09;是一种通过分解复杂问题为重叠子问题&#xff0c;并存储子问题的解以避免重复计算&#xff0c;从而高效求解具有特定性质&#xff08;重叠子问题、最优子结构&#xff09;问题的算法…

Java基本技术讲解

一、基础语法三要素 暂时无法在飞书文档外展示此内容 &#x1f511; 黄金法则​&#xff1a;每个变量都要声明类型&#xff01;二、程序逻辑控制&#xff08;游戏行为核心&#xff09; 条件判断&#xff1a;if-else - “岔路口选择” // 捡到金币逻辑 if (isTouching(Coin.clas…

【网络基础2】路由器的 “两扇门”:二层接口和三层接口到底有啥不一样?

目录 前言:路由器不是只有 “插网线的口” 一、先搞懂一个基础:路由器是 “网络交通枢纽” 二、二层接口:“小区内部的单元门”,只认 “住户身份证” 1. 啥是二层接口? 2. 用 “小区内部串门” 理解二层接口 步骤 1:手机打包数据,写上 “收件人身份证” 步骤 2:二…

MLIR TableGen

简介 TableGen 是一种领域特定语言&#xff08;DSL&#xff09;&#xff0c;TableGen 的设计目标是允许编写灵活的描述&#xff0c;并将记录的通用特性提取出来&#xff0c;从而减少重复代码并提高代码的可维护性。 TableGen的工作流程&#xff1a; 前端解析&#xff1a; Ta…

2、docker容器命令 | 信息查看

1、命令总览命令作用docker ps查看运行中的容器&#xff08;-a查看所有容器&#xff09;docker logs [CONTAINER]查看容器日志&#xff08;-f实时追踪日志&#xff09;docker inspect [CONTAINER]查看容器详细信息&#xff08;JSON格式&#xff09;docker stats [CONTAINER]实时…

【MySQL】MySQL中锁有哪些?

一、按照粒度分类&#xff1a; 粒度越小&#xff0c;并发度越高&#xff0c;锁开销越大。 1.全局锁&#xff1a; 作用&#xff1a; 锁定整个MySQL实例(所有数据库)。适用场景&#xff1a; 全库逻辑部分。(确保备份期间数据的一致性。)实现方式&#xff1a; 通过 FLUSH TABLES W…

语义分割--deeplabV3+

根据论文网络结构图讲一下&#xff1a;网络分为两部分&#xff1a;encoder和decoder部分。 Encoder&#xff1a;DCNN就是主干网络&#xff0c;例如resnet&#xff0c;Xception&#xff0c;MobileNet这些&#xff08;主干网络也要使用空洞卷积&#xff09;&#xff0c;对dcnn的结…

Azure DevOps 中的代理

必知词汇 深入研究 Azure DevOps 中的代理之前需要掌握的基本概念: 代理:Azure DevOps 中的代理是一个软件组件,负责执行流水线中的任务和作业。这可能包括数据中心内的物理服务器、本地或云端托管的虚拟机,甚至是容器化环境。这些代理可以在各种操作系统和环境中运行,例如…

AUTOSAR进阶图解==>AUTOSAR_SRS_ADCDriver

AUTOSAR ADC驱动详解 基于AUTOSAR标准的ADC驱动模块需求规范分析目录 ADC驱动模块概述 关键概念定义 ADC驱动架构 ADC驱动在AUTOSAR分层架构中的位置ADC驱动的主要职责 ADC驱动配置结构 通用配置(AdcGeneral)硬件单元配置(AdcHwUnit)通道配置(AdcChannel)通道组配置(AdcChanne…

宝马集团与SAP联合打造生产物流数字化新标杆

在德国雷根斯堡的宝马工厂&#xff0c;每57秒就有一辆新车下线。这座工厂不仅是汽车制造的基地&#xff0c;更是宝马集团向SAP S/4HANA云平台转型的先锋项目。通过“RISE with SAP”计划&#xff0c;宝马将该工厂的运营系统全面迁移至SAP S/4HANA Cloud Private Edition&#x…

Go 语言实战:构建一个高性能的 MySQL + Redis 应用

引言&#xff1a;为什么是 Go MySQL Redis&#xff1f;在现代后端技术栈中&#xff0c;Go MySQL Redis 的组合堪称“黄金搭档”&#xff0c;被广泛应用于各种高并发业务场景。Go 语言&#xff1a;以其卓越的并发性能、简洁的语法和高效的执行效率&#xff0c;成为构建高性能…

Excel超级处理器,多个word表格模板中内容提取到Excel表格中

在职场中&#xff0c;很多人习惯在word里插入表格&#xff0c;设计模板&#xff0c;填写内容&#xff0c;一旦有多个word文件需要整理在excel表格中&#xff0c;最常见的工作方式就是每个word文件打开&#xff0c;复制&#xff0c;粘贴到excel表格里&#xff0c;这样的工作方式…

前端工程化:ES6特性

本文为个人学习笔记整理&#xff0c;仅供交流参考&#xff0c;非专业教学资料&#xff0c;内容请自行甄别 文章目录一、let与var1.1、越狱问题1.2、变量的重复声明1.3、变量提升问题二、解构2.1、数组解构2.2、对象解构2.3、方法解构三、链判断四、参数默认值五、箭头函数六、模…

大屏项目展示

一、项目克隆与基础操作 我们参考的项目 互联网设备可视化平台---IofTV-Screen: 🔥一个基于 vue、datav、Echart 框架的物联网可视化(大屏展示)模板,提供数据动态刷新渲染、屏幕适应、数据滚动配置,内部图表自由替换、Mixins注入等功能,持续更新.... 将次项目克隆到本…

基于R语言地理加权回归、主成份分析、判别分析等空间异质性数据分析实践技术应用

在自然和社会科学领域有大量与地理或空间有关的数据&#xff0c;这一类数据一般具有严重的空间异质性&#xff0c;而通常的统计学方法并不能处理空间异质性&#xff0c;因而对此类型的数据无能为力。以地理加权回归为基础的一系列方法&#xff1a;经典地理加权回归&#xff0c;…

【Flask 基础 ①】 | 路由、参数与模板渲染

0 序言 Flask 是 Python 生态中一款轻量级 Web 框架&#xff0c;以简洁、灵活著称。 学习 Flask 的意义在于&#xff1a; 快速开发&#xff1a;通过少量代码即可搭建功能完整的 Web 应用&#xff1b;理解原理&#xff1a;其设计清晰体现了 Web 框架的核心逻辑&#xff0c;如路由…