{x}
blog image

Jump Game VIII

Solution Explanation: Jump Game VIII

This problem asks for the minimum cost to reach the last index of an array, given constraints on valid jumps and costs associated with each index. The solution uses a combination of a monotonic stack and dynamic programming.

1. Monotonic Stack for Finding Valid Jumps:

The core idea is to efficiently identify all valid jumps from a given index. The constraints specify that a jump from i to j is valid only if the sequence of numbers between i and j is either strictly decreasing (if nums[i] > nums[j]) or strictly increasing (if nums[i] <= nums[j]).

We use two monotonic stacks:

  • Decreasing Stack: This stack maintains indices in decreasing order of nums values. When processing an index i, we pop elements from the stack as long as their nums values are less than nums[i]. The last popped element (if any) represents the farthest index with a smaller nums value that's reachable from i—a valid jump according to the problem constraints.
  • Increasing Stack: This works similarly, but for indices in increasing order of nums values. It finds the farthest reachable index with a larger (or equal) nums value.

These stacks efficiently identify valid jump targets for each index in O(n) time, avoiding nested loops.

2. Adjacency List (Graph Representation):

After using the monotonic stacks, we construct an adjacency list to represent the graph of possible jumps. g[i] stores the indices that can be jumped to from index i.

3. Dynamic Programming for Minimum Cost:

Finally, dynamic programming is used to find the minimum cost. f[i] represents the minimum cost to reach index i.

  • Initialization: f[0] = 0 (starting at index 0 with zero cost). Other f[i] values are initialized to infinity.
  • Iteration: We iterate through the indices. For each index i, we examine each j in g[i] (each valid jump from i). We update f[j] using the formula: f[j] = min(f[j], f[i] + costs[j]). This means the cost to reach j is the minimum between its current cost and the cost to reach i plus the cost to jump to j.
  • Result: f[n - 1] contains the minimum cost to reach the last index.

Time Complexity: O(n) - The monotonic stacks and the dynamic programming iteration each take linear time.

Space Complexity: O(n) - To store the adjacency list g and the DP array f.

Code Example (Python):

from collections import defaultdict
import math
 
class Solution:
    def minCost(self, nums: List[int], costs: List[int]) -> int:
        n = len(nums)
        g = defaultdict(list)
        
        # Decreasing stack
        stk = []
        for i in range(n - 1, -1, -1):
            while stk and nums[stk[-1]] < nums[i]:
                stk.pop()
            if stk:
                g[i].append(stk[-1])
            stk.append(i)
 
        # Increasing stack
        stk = []
        for i in range(n - 1, -1, -1):
            while stk and nums[stk[-1]] >= nums[i]:
                stk.pop()
            if stk:
                g[i].append(stk[-1])
            stk.append(i)
 
        f = [math.inf] * n  # Initialize DP array
        f[0] = 0
        for i in range(n):
            for j in g[i]:
                f[j] = min(f[j], f[i] + costs[j])
        return f[n - 1]
 

The code in other languages (Java, C++, Go, TypeScript) follows the same logic, with appropriate adjustments for syntax and data structures. The key is the efficient use of monotonic stacks for jump identification and the subsequent dynamic programming for cost minimization.