leetcode
leetcode 1501 ~ 1550
跳跃游戏 VI

跳跃游戏 VI

难度:

标签:

题目描述

代码结果

运行时间: 88 ms, 内存: 27.5 MB


/*
 * 思路:
 * 使用Java Stream无法直接实现此算法,因为它涉及到动态规划和双端队列的操作。
 * 但是我们可以使用Java Stream的api来简化数组的初始化等步骤。
 */
import java.util.*;
import java.util.stream.*;

public class MaxScoreStream {
    public int maxResult(int[] nums, int k) {
        int n = nums.length;
        int[] dp = new int[n];
        dp[0] = nums[0];
        Deque<Integer> deque = new LinkedList<>();
        deque.add(0);

        for (int i = 1; i < n; i++) {
            dp[i] = nums[i] + dp[deque.peekFirst()];
            while (!deque.isEmpty() && dp[i] >= dp[deque.peekLast()]) {
                deque.pollLast();
            }
            deque.addLast(i);
            if (i - deque.peekFirst() >= k) {
                deque.pollFirst();
            }
        }

        return dp[n - 1];
    }
}

解释

方法:

这道题的核心思路是使用动态规划(DP)结合单调队列优化。DP数组表示到达每个位置i的最大得分。为了避免重复计算每个位置最大得分时所有可能的前驱位置的得分,我们使用一个单调队列来优化这个过程。队列中存储的是索引,且保证队头是最大的DP值的索引。遍历数组时,每到一个正数或者数组最后一个元素,检查是否能直接从上一个正数位置跳到当前位置。如果不能,就用单调队列来计算从上一个正数位置到当前位置的最大得分,更新中间的DP值,并更新队列以保持单调递减。总的来说,此题解通过减少重复计算并快速访问当前最大得分,从而降低了时间复杂度。

时间复杂度:

O(n)

空间复杂度:

O(k)

代码细节讲解

🦆
题解中提到当`k=1`时直接返回整个数组的和,这种方法是否忽略了数组中可能存在的负数对总和的影响?
题解在此处的确简化了问题处理。实际上,当`k=1`时,由于每一步只能向前移动一位,因此无论正负,你都必须走过数组中的每一个数。因此直接求和是正确的,因为你不能跳过任何元素,包括负数。这种方法并没有真正忽略负数的影响,而是反映了每步都必须经过的情况,即使这些步骤中包含负数。
🦆
单调队列的使用在这种情况下如何确保队头始终是最大的DP值的索引?能否详细解释这一过程?
单调队列是按照值的单调递减顺序来维持元素的。在算法中,每当添加一个新的元素到单调队列时,会从队尾开始比较,把所有小于等于当前元素值的索引从队尾移除。这样做确保了队列里剩下的都是比当前元素大的值,且队头是所有这些元素中最大的。当超出跳跃范围`k`时,队首元素会被移除。这样,队头始终是在允许跳跃范围内的最大DP值的索引。
🦆
题解中提到,如果当前数字是非负数或者是数组的最后一个元素时,会检查是否能直接从上一个非负数位置跳到当前位置。这里的逻辑是否意味着在两个非负数之间的负数会被忽略?这样做的原因是什么?
这里并不是忽略了负数,而是对策略进行了优化。题解中的策略是在遇到非负数或数组最后一个元素时,尝试直接跳跃,如果不行(即两者之间的间隔超过了k),则使用单调队列来计算最优路径。这种做法是为了减少计算复杂度,因为非负数自然会增加得分,优先从这些点跳跃可能直接获得更高的分数。在不能直接跳跃的情况下,通过单调队列处理保证了即使存在负数,也能计算到最优解。

相关问题