LeetCode 2297. 跳跃游戏 VIII(中等)

题目描述

给定一个长度为 n 的下标从 0 开始的整数数组 nums。初始位置为下标 0。当 i < j 时,你可以从下标 i 跳转到下标 j:

  • 对于在 i < k < j 范围内的所有下标 k 有 nums[i] <= nums[j] 和 nums[k] < nums[i] , 或者
  • 对于在 i < k < j 范围内的所有下标 k 有 nums[i] > nums[j] 和 nums[k] >= nums[i] 。

你还得到了一个长度为 n 的整数数组 costs,其中 costs[i] 表示跳转下标 i 的代价。

返回跳转到下标 n - 1 的最小代价。

示例 1:

输入: nums = [3,2,4,4,1], costs = [3,7,6,4,2]
输出: 8
解释: 从下标 0 开始。
- 以 costs[2]= 6 的代价跳转到下标 2。
- 以 costs[4]= 2 的代价跳转到下标 4。
总代价是 8。可以证明,8 是所需的最小代价。
另外两个可能的路径是:下标 0 -> 1 -> 4 和下标 0 -> 2 -> 3 -> 4。
它们的总代价分别为9和12。

示例 2:

输入: nums = [0,1,2], costs = [1,1,1]
输出: 2
解释: 从下标 0 开始。
- 以 costs[1] = 1 的代价跳转到下标 1。
- 以 costs[2] = 1 的代价跳转到下标 2。
总代价是 2。注意您不能直接从下标 0 跳转到下标 2,因为 nums[0] <= nums[1]。

解释:

  • n == nums.length == costs.length
  • 1 <= n <= 10^5
  • 0 <= nums[i], costs[i] <= 10^5

问题分析

这道题是跳跃游戏系列中的一道具有挑战性的题目。关键在于理解跳跃条件:

跳跃条件解析:

  • 条件一: nums[i] <= nums[j] 且中间所有 nums[k] < nums[i]

    • 含义:从位置i跳到右侧第一个 ≥ nums[i] 的位置

    • 对应:单调递减栈(维护一个从栈底到栈顶递减的序列)

  • 条件二: nums[i] > nums[j] 且中间所有 nums[k] >= nums[i]

    • 含义:从位置i跳到右侧第一个 < nums[i] 的位置

    • 对应:单调递增栈(维护一个从栈底到栈顶递增的序列)

这两个条件实际上是互补的,可以用单调栈来高效处理。


解题思路

单调栈 + 动态规划

核心思想:

  1. 使用两个单调栈分别处理两种跳跃条件
  2. 用动态规划记录到达每个位置的最小代价
  3. 从左到右遍历,动态更新可达位置的最小代价

算法步骤:

  • 初始化 dp[i] 表示到达位置 i 的最小代价,dp[0] = 0
  • 维护两个单调栈:
    • stack1:处理条件一(单调递减栈)
    • stack2:处理条件二(单调递增栈)
  • 对每个位置 j,检查能从哪些位置跳到 j,并更新最小代价

算法过程

输入: nums = [3,2,4,4,1], costs = [3,7,6,4,2]

初始状态

位置:    0  1  2  3  4
nums:   [3, 2, 4, 4, 1]
costs:  [3, 7, 6, 4, 2]
dp:     [0, ∞, ∞, ∞, ∞]stack1: []  (单调递减栈 - 处理条件一)
stack2: []  (单调递增栈 - 处理条件二)

步骤1:处理位置0 (nums[0] = 3)

当前位置 j = 0, nums[0] = 3stack1操作:
- 栈为空,直接入栈
- stack1: [0]stack2操作:
- 栈为空,直接入栈  
- stack2: [0]dp状态:[0, ∞, ∞, ∞, ∞]

步骤2:处理位置1 (nums[1] = 2)

当前位置 j = 1, nums[1] = 2stack1操作(条件一):
- nums[stack1.top()] = nums[0] = 3 > nums[1] = 2
- 不满足 nums[i] <= nums[j],不弹出
- stack1: [0, 1]stack2操作(条件二):
- nums[stack2.top()] = nums[0] = 3 > nums[1] = 2 ✓
- 弹出位置0,更新 dp[1] = min(∞, dp[0] + costs[1]) = min(∞, 0 + 7) = 7
- 将位置1入栈
- stack2: [1]dp状态:[0, 7, ∞, ∞, ∞]跳跃路径:0 → 1 (代价7)

步骤3:处理位置2 (nums[2] = 4)

当前位置 j = 2, nums[2] = 4stack1操作(条件一):
- nums[1] = 2 <= nums[2] = 4 ✓,弹出位置1dp[2] = min(∞, dp[1] + costs[2]) = min(∞, 7 + 6) = 13
- nums[0] = 3 <= nums[2] = 4 ✓,弹出位置0  dp[2] = min(13, dp[0] + costs[2]) = min(13, 0 + 6) = 6
- 将位置2入栈
- stack1: [2]stack2操作(条件二):
- nums[1] = 2 < nums[2] = 4,不满足条件二,不弹出
- 将位置2入栈
- stack2: [1, 2]dp状态:[0, 7, 6, ∞, ∞]跳跃路径:0 → 2 (代价6) 或 0 → 1 → 2 (代价13)
最优:0 → 2 (代价6)

步骤4:处理位置3 (nums[3] = 4)

当前位置 j = 3, nums[3] = 4stack1操作(条件一):
- nums[2] = 4 <= nums[3] = 4 ✓,弹出位置2dp[3] = min(∞, dp[2] + costs[3]) = min(∞, 6 + 4) = 10
- 将位置3入栈
- stack1: [3]stack2操作(条件二):
- nums[2] = 4 不大于 nums[3] = 4,不弹出
- 将位置3入栈
- stack2: [1, 2, 3]dp状态:[0, 7, 6, 10, ∞]跳跃路径:0 → 2 → 3 (代价10)

步骤5:处理位置4 (nums[4] = 1)

当前位置 j = 4, nums[4] = 1stack1操作(条件一):
- nums[3] = 4 > nums[4] = 1,不满足条件一,不弹出
- 将位置4入栈
- stack1: [3, 4]stack2操作(条件二):
- nums[3] = 4 > nums[4] = 1 ✓,弹出位置3dp[4] = min(∞, dp[3] + costs[4]) = min(∞, 10 + 2) = 12
- nums[2] = 4 > nums[4] = 1 ✓,弹出位置2dp[4] = min(12, dp[2] + costs[4]) = min(12, 6 + 2) = 8
- nums[1] = 2 > nums[4] = 1 ✓,弹出位置1dp[4] = min(8, dp[1] + costs[4]) = min(8, 7 + 2) = 9
- 最终 dp[4] = 8
- stack2: [4]dp状态:[0, 7, 6, 10, 8]跳跃路径:0 → 2 → 4 (代价8)

最优路径

最终答案:dp[4] = 8最优路径:0 → 2 → 4
详细分析:
- 从位置0跳到位置2:满足条件一 (nums[0]=3 <= nums[2]=4,中间nums[1]=2 < nums[0]=3)
- 从位置2跳到位置4:满足条件二 (nums[2]=4 > nums[4]=1,中间nums[3]=4 >= nums[2]=4)
- 总代价:costs[2] + costs[4] = 6 + 2 = 8

详细代码实现

Java 实现

class Solution {public long minCost(int[] nums, int[] costs) {int n = nums.length;long[] dp = new long[n];// 初始化dp数组Arrays.fill(dp, Long.MAX_VALUE);dp[0] = 0;  // 起始位置代价为0// 两个单调栈Deque<Integer> stack1 = new ArrayDeque<>();  // 处理条件一Deque<Integer> stack2 = new ArrayDeque<>();  // 处理条件二for (int j = 0; j < n; j++) {// 处理条件一:nums[i] <= nums[j] 且中间元素都小于 nums[i]// 单调递减栈,当遇到更大的元素时弹出while (!stack1.isEmpty() && nums[stack1.peek()] <= nums[j]) {int i = stack1.pop();dp[j] = Math.min(dp[j], dp[i] + costs[j]);}stack1.push(j);// 处理条件二:nums[i] > nums[j] 且中间元素都大于等于 nums[i]// 单调递增栈,当遇到更小的元素时弹出while (!stack2.isEmpty() && nums[stack2.peek()] > nums[j]) {int i = stack2.pop();dp[j] = Math.min(dp[j], dp[i] + costs[j]);}stack2.push(j);}return dp[n - 1];}
}

C# 实现

public class Solution {public long MinCost(int[] nums, int[] costs) {int n = nums.Length;long[] dp = new long[n];// 初始化dp数组for (int i = 0; i < n; i++) {dp[i] = long.MaxValue;}dp[0] = 0;  // 起始位置代价为0// 两个单调栈Stack<int> stack1 = new Stack<int>();  // 处理条件一Stack<int> stack2 = new Stack<int>();  // 处理条件二for (int j = 0; j < n; j++) {// 处理条件一:nums[i] <= nums[j] 且中间元素都小于 nums[i]while (stack1.Count > 0 && nums[stack1.Peek()] <= nums[j]) {int i = stack1.Pop();dp[j] = Math.Min(dp[j], dp[i] + costs[j]);}stack1.Push(j);// 处理条件二:nums[i] > nums[j] 且中间元素都大于等于 nums[i]while (stack2.Count > 0 && nums[stack2.Peek()] > nums[j]) {int i = stack2.Pop();dp[j] = Math.Min(dp[j], dp[i] + costs[j]);}stack2.Push(j);}return dp[n - 1];}
}

时间复杂度和空间复杂度

  • 时间复杂度:O(n) - 每个元素最多入栈和出栈一次
  • 空间复杂度:O(n) - dp数组和两个栈的空间

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

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

相关文章

【前端】缓存相关

本知识页参考&#xff1a;https://zhuanlan.zhihu.com/p/586060532 1. 概述 1.1 应用场景 静态资源 场景&#xff1a;图片、CSS、JS 文件等静态资源实现&#xff1a;使用 HTTP 缓存控制头&#xff0c;或者利用 CDN 进行边缘缓存 数据缓存 场景&#xff1a;请求的返回结果实现…

猎板硬金镀层厚度:高频通信领域的性能分水岭

在 5G 基站、毫米波雷达等高频场景中&#xff0c;硬金镀层厚度的选择直接决定了 PCB 的信号完整性与长期可靠性。猎板硬金工艺&#xff1a; 1.8μm 金层搭配罗杰斯 4350B 基材的解决方案&#xff0c;在 10GHz 频段实现插入损耗&#xff1c;0.15dB/cm&#xff0c;较常规工艺降低…

第35次CCF计算机软件能力认证-5-木板切割

原题链接&#xff1a; TUOJ 我自己写的35分正确但严重超时的代码 #include <bits/stdc.h> using namespace std; int main() {int n, m, k;cin >> n >> m >> k;vector<unordered_map<int, int>> mp(2);int y;for (int i 1; i < n; …

【蓝桥杯】包子凑数

包子凑数 题目描述 小明几乎每天早晨都会在一家包子铺吃早餐。他发现这家包子铺有 NN 种蒸笼&#xff0c;其中第 ii 种蒸笼恰好能放 AiAi​ 个包子。每种蒸笼都有非常多笼&#xff0c;可以认为是无限笼。 每当有顾客想买 XX 个包子&#xff0c;卖包子的大叔就会迅速选出若干…

pikachu通关教程-目录遍历漏洞(../../)

目录遍历漏洞也可以叫做信息泄露漏洞、非授权文件包含漏洞等. 原理:目录遍历漏洞的原理比较简单&#xff0c;就是程序在实现上没有充分过滤用户输入的../之类的目录跳转符&#xff0c;导致恶意用户可以通过提交目录跳转来遍历服务器上的任意文件。 这里的目录跳转符可以是../…

[概率论基本概念4]什么是无偏估计

关键词&#xff1a;Unbiased Estimation 一、说明 对于无偏和有偏估计&#xff0c;需要了解其叙事背景&#xff0c;是指整体和抽样的关系&#xff0c;也就是说整体的叙事是从理论角度的&#xff0c;而估计器原理是从实践角度说事&#xff1b;为了表明概率理论&#xff08;不可…

面试题——计算机网络:HTTP和HTTPS的区别?

HTTP&#xff08;HyperText Transfer Protocol&#xff09;&#xff1a;作为互联网上应用最广泛的网络通信协议&#xff0c;HTTP是基于TCP/IP协议族的应用层协议。它采用标准的请求-响应模式进行通信&#xff0c;通过简洁的报文格式&#xff08;包含请求行、请求头、请求体等&a…

uni-app学习笔记十九--pages.json全局样式globalStyle设置

pages.json 页面路由 pages.json 文件用来对 uni-app 进行全局配置&#xff0c;决定页面文件的路径、窗口样式、原生的导航栏、底部的原生tabbar 等。 导航栏高度为 44px (不含状态栏)&#xff0c;tabBar 高度为 50px (不含安全区)。 它类似微信小程序中app.json的页面管理部…

SQL思路解析:窗口滑动的应用

目录 &#x1f3af; 问题目标 第一步&#xff1a;从数据中我们能直接得到什么&#xff1f; 第二步&#xff1a;我们想要的“7天窗口”长什么样&#xff1f; 第三步&#xff1a;SQL 怎么表达“某一天的前六天”&#xff1f; &#x1f50d;JOIN 比窗口函数更灵活 第四步&am…

解决MyBatis参数绑定中参数名不一致导致的错误问题

前言 作为一名Java开发者&#xff0c;我在实际项目中曾多次遇到MyBatis参数绑定的问题。其中最常见的一种情况是&#xff1a;在Mapper接口中定义的参数名与XML映射文件中的占位符名称不一致&#xff0c;导致运行时抛出Parameter xxx not found类异常。这类问题看似简单&#x…

黑马程序员TypeScript课程笔记—类型兼容性篇

类型兼容性的说明 因为传入的时候只有一个参数 对象之间的类型兼容性 接口之间的类型兼容性 函数之间的类型兼容性&#xff08;函数参数个数&#xff09; 和对象的兼容性正好相反 函数之间的类型兼容性&#xff08;函数参数类型&#xff09; 函数参数的兼容性就不要从接口角度…

智能电视的操作系统可能具备哪些优势

丰富的应用资源&#xff1a; 操作系统内置了应用商店&#xff0c;提供了丰富的应用资源&#xff0c;涵盖视频、游戏、教育等多个领域&#xff0c;满足不同用户的多样化需求。用户可以轻松下载并安装所需的应用&#xff0c;享受更多元化的娱乐和学习体验。 流畅的操作体验&…

Xget 正式发布:您的高性能、安全下载加速工具!

您可以通过 star 我固定的 GitHub 存储库来支持我&#xff0c;谢谢&#xff01;以下是我的一些 GitHub 存储库&#xff0c;很有可能对您有用&#xff1a; tzst Xget Prompt Library 原文 URL&#xff1a;https://blog.xi-xu.me/2025/06/02/xget-launch-high-performance-sec…

精美的软件下载页面HTML源码:现代UI与动画效果的完美结合

精美的软件下载页面HTML源码&#xff1a;现代UI与动画效果的完美结合 在数字化产品推广中&#xff0c;一个设计精良的下载页面不仅能提升品牌专业度&#xff0c;还能显著提高用户转化率。本文介绍的精美软件下载页面HTML源码&#xff0c;通过现代化UI设计与丰富的动画效果&…

麒麟v10+信创x86处理器离线搭建k8s集群完整过程

前言 最近为某客户搭建内网的信创环境下的x8s集群&#xff0c;走了一些弯路&#xff0c;客户提供的环境完全与互联网分离&#xff0c;通过yum、apt这些直接拉依赖就别想了&#xff0c;用的操作系统和cpu都是国产版本&#xff0c;好在仍然是x86的&#xff0c;不是其他架构&…

Pycharm的使用技巧总结

目录 一、高效便捷的快捷键 二、界面汉化处理 1.设置 2.插件 3.汉化插件安装 三、修改字体大小、颜色 1.选择文件-设置 2.选择编辑器-配色方案-python 3.修改注释行颜色 4.修改编辑器字体颜色 一、高效便捷的快捷键 序号快捷键功能场景效果1Ctrl /快速注释/取消注释…

安全编码规范与标准:对比与分析及应用案例

在软件开发领域&#xff0c;尤其是涉及安全关键系统的开发中&#xff0c;遵循编码规范和标准是确保软件质量和安全性的重要手段。除了CERT C、CERT Java和MISRA外&#xff0c;还有其他多个与安全相关的编码规范和标准&#xff0c;以下是一些主要标准的对比说明&#xff1a; 一…

FFmpeg学习笔记

1. 播放器的架构 2. 播放器的渲染流程 3. ffmpeg下载与安装 3.0 查看PC是否已经安装了ffmpeg ffmpeg 3.1 下载 wget https://ffmpeg.org/releases/ffmpeg-7.0.tar.gz 3.2 解压 tar zxvf ffmpeg-7.0.tar.gz && cd ./ffmpeg-7.0 3.3 查看配置文件 ./configure …

大宽带怎么做

我有10个G的宽带资源&#xff0c;怎样运行P2P才能将收益巨大化&#xff0c;主要有以下几种方式&#xff1a; 1.多设备汇聚模式&#xff1a;使用多台支持千兆网络的服务器或专用PCDN设备&#xff08;如N1盒子&#xff09;&#xff0c;将10条宽带分别接入不同设备&#xff0c;通过…

pytorch基本运算-导数和f-string

引言 在前序对机器学习的探究过程中&#xff0c;我们已经深刻体会到人工智能到处都有微分求导运算&#xff0c;相关文章链接包括且不限于&#xff1a; BP神经网络 逻辑回归 对于pytorch张量&#xff0c;求导运算必不可少&#xff0c;所以本次就专门来学习一下。 f-string的用…