You are given an integer array nums
and an integer k
. Starting at index 0
, you can jump at most k
steps forward at a time. Each time you land on an index i
, you add nums[i]
to your score. Your goal is to reach the last index of the array (index n - 1
) with the maximum possible score.
j
where i < j ≤ i + k
.Key Constraints:
k
).At first glance, the problem looks similar to other "jump game" problems, but with a twist: instead of just checking if you can reach the end, you must maximize your score along the way. The naive approach would be to try every possible path recursively and pick the one with the highest score. However, this is very inefficient because the number of possible paths grows exponentially with the size of the array.
To optimize, we can think in terms of dynamic programming: at each position, what is the best score we could have achieved to get there? This way, we avoid recomputing the same subproblems and only focus on the best possible route to each index.
The challenge then becomes efficiently finding the maximum score among the previous k
positions for each step. A brute-force check of the last k
positions for every index is too slow for large arrays, so we need a way to quickly find the maximum in a sliding window — this is where a deque (double-ended queue) comes in handy.
dp[i]
as the maximum score to reach index i
.i
, dp[i] = nums[i] + max(dp[j])
for j
in [i-k, i-1]
.dp
value in the last k
steps, use a deque to store indices of potential candidates in decreasing order of their dp
values.i
:
< i-k
).dp
value in the window.dp[i] = nums[i] + dp[deque[0]]
.dp
value is less than or equal to dp[i]
(they can't be better for future positions).i
to the deque.dp[n-1]
is the answer.This approach ensures every element is pushed and popped from the deque at most once, giving an efficient solution.
Example: nums = [1, -1, -2, 4, -7, 3]
, k = 2
dp[0] = nums[0] = 1
.i = 1
: Can come from i = 0
. dp[1] = -1 + 1 = 0
.i = 2
: Can come from i = 0
or i = 1
. dp[2] = -2 + max(1, 0) = -1
.i = 3
: Can come from i = 1
or i = 2
. dp[3] = 4 + max(0, -1) = 4
.i = 4
: Can come from i = 2
or i = 3
. dp[4] = -7 + max(-1, 4) = -3
.i = 5
: Can come from i = 3
or i = 4
. dp[5] = 3 + max(4, -3) = 7
.
The maximum score to reach the end is 7
.
At each step, the deque ensures we always know the best previous position to jump from within the window of k
steps.
k
positions, leading to O(nk)
time and O(n)
space.O(n)
. Space is O(n)
for the dp
array and the deque.The optimized approach is much faster and well-suited for large inputs.
The key to solving Jump Game VI efficiently is to combine dynamic programming with a sliding window maximum. By using a deque, we can always access the best score in the last k
steps in constant time, avoiding brute-force checks. This approach is both elegant and efficient, transforming a potentially slow problem into one that runs in linear time.
from collections import deque
class Solution:
def maxResult(self, nums, k):
n = len(nums)
dp = [0] * n
dp[0] = nums[0]
dq = deque([0])
for i in range(1, n):
# Remove indices out of window
while dq and dq[0] < i - k:
dq.popleft()
dp[i] = nums[i] + dp[dq[0]]
# Maintain decreasing order in deque
while dq and dp[i] >= dp[dq[-1]]:
dq.pop()
dq.append(i)
return dp[-1]
#include <vector>
#include <deque>
using namespace std;
class Solution {
public:
int maxResult(vector<int>& nums, int k) {
int n = nums.size();
vector<int> dp(n);
dp[0] = nums[0];
deque<int> dq;
dq.push_back(0);
for (int i = 1; i < n; ++i) {
while (!dq.empty() && dq.front() < i - k)
dq.pop_front();
dp[i] = nums[i] + dp[dq.front()];
while (!dq.empty() && dp[i] >= dp[dq.back()])
dq.pop_back();
dq.push_back(i);
}
return dp[n - 1];
}
};
import java.util.*;
class Solution {
public int maxResult(int[] nums, int k) {
int n = nums.length;
int[] dp = new int[n];
dp[0] = nums[0];
Deque<Integer> dq = new ArrayDeque<>();
dq.addLast(0);
for (int i = 1; i < n; i++) {
while (!dq.isEmpty() && dq.peekFirst() < i - k) {
dq.pollFirst();
}
dp[i] = nums[i] + dp[dq.peekFirst()];
while (!dq.isEmpty() && dp[i] >= dp[dq.peekLast()]) {
dq.pollLast();
}
dq.addLast(i);
}
return dp[n - 1];
}
}
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
var maxResult = function(nums, k) {
const n = nums.length;
const dp = Array(n).fill(0);
dp[0] = nums[0];
const dq = [];
dq.push(0);
for (let i = 1; i < n; i++) {
while (dq.length && dq[0] < i - k) {
dq.shift();
}
dp[i] = nums[i] + dp[dq[0]];
while (dq.length && dp[i] >= dp[dq[dq.length - 1]]) {
dq.pop();
}
dq.push(i);
}
return dp[n - 1];
};