[LeetCode 1696] Jump Game VI
You are given a 0indexed integer array nums
and an integer k
.
You are initially standing at index 0
. In one move, you can jump at most k
steps forward without going outside the boundaries of the array. That is, you can jump from index i
to any index in the range [i + 1, min(n  1, i + k)]
inclusive.
You want to reach the last index of the array (index n  1
). Your score is the sum of all nums[j]
for each index j
you visited in the array.
Return the maximum score you can get.
Example 1:
Input: nums = [1,1,2,4,7,3], k = 2
Output: 7
Explanation: You can choose your jumps forming the subsequence [1,1,4,3] (underlined above). The sum is 7.
Example 2:
Input: nums = [10,5,2,4,0,3], k = 3
Output: 17
Explanation: You can choose your jumps forming the subsequence [10,4,3] (underlined above). The sum is 17.
Example 3:
Input: nums = [1,5,20,4,1,3,6,3], k = 2
Output: 0
Constraints:

1 <= nums.length, k <= 10^5
10^4 <= nums[i] <= 10^4
Think about solving this problem backward: if we are at the last position, then of all the positions that can reach the last position in 1 jump, we should pick the position that yields the maximum score. This means we need to solve F(last position  1), F(last position  2),....... F(last position  K). To solve F(last position  1), we need to solve F(last position  2),....... F(last position  K), F(last position  K  1). It is clear that we have overlapping subproblems, so we also use dynamic programming.
Solution 1. O(N * K) DP
dp[i]: the max score we can get at position i;
Answer is dp[N  1].
Solution 2. O(N * logN) DP with max heap
The bottle neck in solution 1 is that we need to repeatedly find the max dp value among the previous K values. We can speed this up by using a max heap.
The max heap's entry stores both dp value and its position. When querying the max dp value, we first pop all the values that are out of the Ksized window. Then we pick a valid max value to update the current dp[i]. The runtime is O(N * logN) since we iterate from 0 to N  1 and for each position it is added to then popped out of the max heap at most once.
class Solution { public int maxResult(int[] nums, int k) { int n = nums.length; int[] dp = new int[n]; PriorityQueue<int[]> q = new PriorityQueue<>(Comparator.comparingInt(e>e[0])); for(int i = 0; i < n; i++) { while(q.size() > 0 && q.peek()[1] + k < i) { q.poll(); } dp[i] = nums[i] + (q.size() > 0 ? q.peek()[0] : 0); q.add(new int[]{dp[i], i}); } return dp[n  1]; } }
Solution 3. O(N) DP with double ended queue
Can we do better than O(N * logN)? Think about what happens when the current dp[i] is bigger than dp[i  1]. Since dp[i] appears after dp[i  1] and is better than dp[i  1], this means we can ignore dp[i  1] for the rest of our dp iteration. In general, we can safely discard all previous <= dp values. We still need to keep previous > dp values until they are out of the Ksized window. This calls for a double ended queue. The head has the current max dp value. We remove out of window positions from the head of the dq and remove <= dp values and add newly computed dp value at the end of the dq.
class Solution { public int maxResult(int[] nums, int k) { int n = nums.length; int[] dp = new int[n]; ArrayDeque<Integer> q = new ArrayDeque<>(); for(int i = 0; i < n; i++) { while(q.size() > 0 && q.peekFirst() < i  k) { q.removeFirst(); } dp[i] = nums[i] + (q.size() > 0 ? dp[q.peekFirst()] : 0); while(q.size() > 0 && dp[q.peekLast()] <= dp[i]) { q.removeLast(); } q.addLast(i); } return dp[n  1]; } }