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

[LeetCode 1696] 跳跃游戏 6(Ⅵ)

题面:

LeetCode 1696
在这里插入图片描述

数据范围:

1 ≤ n u m s . l e n g t h , k ≤ 1 0 5 1 \le nums.length, \ k \le 10^5 1nums.length, k105
− 1 0 4 ≤ n u m s [ i ] ≤ 1 0 4 -10^4 \le nums[i] \le 10^4 104nums[i]104

思路 & Code

重点: 可以从下标 i i i 跳到 [ i + 1 , m i n ( n − 1 , i + k ) ] [i+1,min(n-1,i+k)] [i+1,min(n1,i+k)] 区间内的任意一点;得分是所跳到路径的 n u m s nums nums 总和;要求最大得分。

首先自然想到动态规划:设 f [ i ] f[i] f[i] 维护到点 i i i 的最大得分,则 f [ i ] f[i] f[i] 必然是由 j ∈ { 0 , 1 , . . . p ( p < i ) } j\in \{0,1,...p\ (p<i)\} j{0,1,...p (p<i)} 中的一个 f [ j ] f[j] f[j] 转移过来的,并且 j + k ≥ i j+k\ge i j+ki。直接暴力DP呢,包超的, O ( n 2 ) O(n^2) O(n2)
但如果只记录前序的最大值 m a x S c o r e maxScore maxScore,却无法判定得到该值的下标 p r e pre pre 是否能够跳到当前点 i i i
接下来就得想优化策略啦。

1. DP + 最大堆

时间复杂度: O ( n l o g k ) O(nlogk) O(nlogk)
空间复杂度: O ( n ) O(n) O(n)

因为要求最大得分,又是根据前置路径来更新贡献的,所以可以很自然地想到用最大堆去维护前序最大得分,且从下标 0 0 0 开始顺序遍历(保证当前点 i i i 的答案是从前序点过来的)。由于有步长限制 k k k,我们必须判断获得最大得分的点 p r e pre pre 是否能够跳到当前点 n o w now now,如果其不能跳到,则说明后续所有点的贡献里都不会有 p r e pre pre 点的贡献。
故可以用 p a i r ( m a x S c o r e , i n d e x ) pair(maxScore, index) pair(maxScore,index) 来作为最大堆的元素。

Code:

int maxResult(vector<int>& nums, int k) {int n =nums.size();priority_queue<pair<int, int>> q; //优先队列维护最大堆q.push({nums[0], 0});for(int i = 1; i < n; ++i) {//筛选能跳到当前点的最大贡献while(!q.empty() && q.top().second < i - k) q.pop();//更新最大堆q.push({q.top().first + nums[i], i});}while(!q.empty() && q.top().second != n-1) q.pop();return q.top().first;
}

2. DP + 双端队列维护滑动窗口

这就不得不提到非常经典的一道题了 LeetCode 239 滑动窗口最大值

时间复杂度: O ( n ) O(n) O(n)
上面提到只存前序最大值而没有其下标是不行的。这边可以想到用滑动窗口去维护 { i − k , i − k + 1 , . . . , i − 1 } \{i-k,i-k+1,...,i-1\} {ik,ik+1,...,i1} 总共 k k k“最大值”。滑动窗口从队首到队尾的索引依次增大,但是对应的值依次递减,即对于队列 { r 1 , r 2 , . . . , r n } \{r_1, r_2, ..., r_n\} {r1,r2,...,rn},一定有 d p [ r 1 ] > d p [ r 2 ] > . . . > d p [ r n ] dp[r_1] > dp[r_2] > ... > dp[r_n] dp[r1]>dp[r2]>...>dp[rn],且 { r 1 < r 2 < r 3 < . . . < r n } \{ r_1 < r_2 < r_3 <... < r_n \} {r1<r2<r3<...<rn}

遍历时,需要动态维护滑动窗口:

  1. 当滑动窗口的左边界(最小下标)已经跳不到当前点了,则直接剔除。
  2. 滑动窗口内只保留贡献(最大值)比当前点还大的元素,其他全部剔除。这是由于如果滑动窗口内小于等于当前点贡献的元素,对后续是一定没有贡献的。假设 d p [ j ] ≤ d p [ i ] , j ∈ { i − k , i − k + 1 , . . . , i − 1 } dp[j] \le dp[i]\ ,j \in \{ i-k, i-k+1,...,i-1\} dp[j]dp[i] ,j{ik,ik+1,...,i1},首先在滑动窗口向右移动的时候 d p [ j ] dp[j] dp[j] 一定是先被踢出窗口的;其次,当要取到最大值 d p [ i ] = = d p [ j ] dp[i] == dp[j] dp[i]==dp[j] 的时候,由于 i > j i > j i>j d p [ i ] dp[i] dp[i] 一定是比 d p [ j ] dp[j] dp[j] 能贡献到更后面的下标的(也就是 i i i 跳的比 j j j 远),所以只保留 i i i 即可。
  3. 将当前点的贡献和下标一起加入滑动窗口末尾

Code:

int maxResult(vector<int>& nums, int k) {int n = nums.size(), maxScore = 0;deque<pair<int, int>> dq;dq.emplace_back(nums[0], 0);maxScore = nums[0];for(int i = 1; i < n; ++i) {if(dq.front().second < i - k) dq.pop_front();maxScore = dq.front().first + nums[i];while(!dq.empty() && maxScore >= dq.back().first)dq.pop_back();dq.emplace_back(maxScore, i);}return maxScore;
}

相关文章:

  • Javascript逗号操作符
  • 【JavaScript】十九、页面尺寸事件 + 获取元素位置
  • (二十五)安卓开发一个完整的登录页面-支持密码登录和手机验证码登录
  • Android Studio 项目文件夹结构详解
  • Android WebView深度性能优化方案
  • UE5 Chaos :官方文献总结 + 渲染网格体 (Render Mesh) 和模拟网格体 是如何关联的?为什么模拟网格体 可以驱动渲染网格体?
  • CAD-MLLM 论文阅读笔记
  • [redis进阶二]分布式系统之主从复制结构(2)
  • 【LeetCode 热题 100】哈希 系列
  • 调节磁盘和CPU的矛盾——InnoDB的Buffer Pool
  • 安全人员如何对漏洞进行定级?
  • HTTP:六.HTTP代理相关介绍
  • 力扣HOT100——无重复字符的最长子字符串
  • route
  • 基于javaweb的SpringBoot影视播放评分交流系统设计与实现(源码+部署文档)
  • 【VsCode】设置文件自动保存
  • Mysql 身份认证绕过漏洞
  • Kotlin 集合过滤全指南:all、any、filter 及高级用法
  • 二叉树的基本功能实现
  • Sentinel源码—1.使用演示和简介一
  • 俄方证实俄总统普京正在会见美特使威特科夫
  • 贵州通报9起群众身边不正之风和腐败问题典型案例
  • 两部门:推动“青年驿站”为毕业生跨地区求职提供住宿优惠便利
  • 瑞士外长答澎湃:瑞中都愿升级自贸协定,关税战没有任何好处
  • 外交部否认中美就关税问题进行磋商谈判
  • 俄罗斯戏剧《大师与玛格丽特》来沪,剧长8小时一天内演完