LeetCode 2786 - Visit Array Positions to Maximize Score
The problem presents a 0-indexed array nums of integers and a positive integer x. You start at the first element, nums[0], and can move to any subsequent position j where j i. For each element you visit, you accumulate its value as part of your score.
Difficulty: 🟡 Medium
Topics: Array, Dynamic Programming
Solution
Problem Understanding
The problem presents a 0-indexed array nums of integers and a positive integer x. You start at the first element, nums[0], and can move to any subsequent position j where j > i. For each element you visit, you accumulate its value as part of your score. However, if the number at your current position and the number at the next position have different parities (one is odd, the other is even), you incur a penalty of x. The goal is to determine the maximum possible total score you can achieve following these movement rules.
The input array can be large (2 <= nums.length <= 10^5), and element values and x can go up to 10^6. This implies that a brute-force solution examining all sequences of positions is infeasible. Instead, we must find an efficient approach that accounts for parity transitions and accumulates the maximum score efficiently.
Important edge cases include arrays where all elements are even or odd, arrays with alternating parity, and large penalty values where avoiding parity changes may be optimal.
Approaches
Brute Force Approach
A naive approach is to recursively explore every valid sequence of positions starting from index 0. For each move from i to j, you calculate the score of nums[j] and subtract x if the parity differs. You maintain the maximum score across all paths. This method works because it considers every possible sequence and applies the penalty exactly as described.
The problem with brute force is its exponential complexity. With an array of length n, there are 2^(n-1) potential paths (each element can be included or skipped), which is completely infeasible for n up to 10^5.
Optimal Approach
The key insight is to track the maximum score achievable ending with an even number and ending with an odd number as we iterate through the array. We only need two values: max_even and max_odd. For each nums[i], the maximum score if we end at i is either adding it to the best sequence of the same parity (no penalty) or to the best sequence of opposite parity minus x (penalty). This reduces the problem to linear time and constant space.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(2^n) | O(n) | Explore all sequences recursively |
| Optimal | O(n) | O(1) | Track only max_even and max_odd, update iteratively |
Algorithm Walkthrough
- Initialize two variables,
max_evenandmax_odd. Ifnums[0]is even, setmax_even = nums[0]andmax_odd = -inf. Otherwise, setmax_odd = nums[0]andmax_even = -inf. - Iterate through the array starting from index 1.
- For each number
nums[i], check its parity. - If it is even, calculate the potential score by either adding it to
max_even(no penalty) ormax_odd - x(penalty for switching parity). Updatemax_evento the maximum of these two values. - If it is odd, calculate the potential score by either adding it to
max_odd(no penalty) ormax_even - x(penalty for switching parity). Updatemax_oddto the maximum of these two values. - Continue this process until the end of the array.
- The result is the maximum of
max_evenandmax_odd, which represents the best achievable score regardless of ending parity.
Why it works: This approach works because we only need to consider the best sequence ending with each parity at each step. Any longer sequence ending at i with the same parity can be built by extending the optimal sequence of the previous parity, ensuring that no optimal path is missed.
Python Solution
from typing import List
class Solution:
def maxScore(self, nums: List[int], x: int) -> int:
max_even = float('-inf')
max_odd = float('-inf')
if nums[0] % 2 == 0:
max_even = nums[0]
else:
max_odd = nums[0]
for num in nums[1:]:
if num % 2 == 0:
max_even = max(max_even + num, max_odd - x + num)
else:
max_odd = max(max_odd + num, max_even - x + num)
return max(max_even, max_odd)
The implementation begins by initializing max_even and max_odd based on the parity of the first element. We then iterate through the array, updating the maximum scores for even and odd endings by considering both extending the same parity or switching parity (and subtracting x). The final answer is the maximum of the two.
Go Solution
func maxScore(nums []int, x int) int64 {
var maxEven, maxOdd int64
maxEven, maxOdd = -1<<60, -1<<60
if nums[0]%2 == 0 {
maxEven = int64(nums[0])
} else {
maxOdd = int64(nums[0])
}
for i := 1; i < len(nums); i++ {
num := int64(nums[i])
if num%2 == 0 {
newEven := max(maxEven + num, maxOdd - int64(x) + num)
maxEven = newEven
} else {
newOdd := max(maxOdd + num, maxEven - int64(x) + num)
maxOdd = newOdd
}
}
return max(maxEven, maxOdd)
}
func max(a, b int64) int64 {
if a > b {
return a
}
return b
}
In Go, we handle potential integer overflows by using int64. The logic mirrors the Python version, using a helper max function to simplify comparisons.
Worked Examples
Example 1: nums = [2,3,6,1,9,2], x = 5
| i | num | max_even | max_odd | Explanation |
|---|---|---|---|---|
| 0 | 2 | 2 | -inf | Initialize |
| 1 | 3 | 2 | max(3 + -inf, 2 - 5 + 3) = 0 | Switching parity incurs penalty |
| 2 | 6 | max(2 + 6, 0 - 5 + 6) = 8 | 0 | Extend even or switch from odd |
| 3 | 1 | 8 | max(0 + 1, 8 - 5 + 1) = 4 | Extend odd or switch from even |
| 4 | 9 | 8 | max(4 + 9, 8 - 5 + 9) = 12 | Choose best |
| 5 | 2 | max(8 + 2, 12 - 5 + 2) = 12 | Extend even or switch |
Final result: max(12,12) = 12
Example 2: nums = [2,4,6,8], x = 3
| i | num | max_even | max_odd |
|---|---|---|---|
| 0 | 2 | 2 | -inf |
| 1 | 4 | max(2+4, -inf-3+4) = 6 | -inf |
| 2 | 6 | max(6+6, -inf-3+6) = 12 | -inf |
| 3 | 8 | max(12+8, -inf-3+8) = 20 | -inf |
Final result: 20
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Single pass through the array, constant work per element |
| Space | O(1) | Only two variables tracked, no additional data structures |
The algorithm scales linearly with input size and uses minimal extra memory, suitable for large arrays.
Test Cases
# Provided examples
assert Solution().maxScore([2,3,6,1,9,2], 5) == 13 # mix of parities
assert Solution().maxScore([2,4,6,8], 3) == 20 # all same parity
# Boundary values
assert Solution().maxScore([1,1], 1) == 2 # smallest array, both odd
assert Solution().maxScore([2,1], 10) == 2 # penalty larger than gain, skip switch
# Alternating parity
assert Solution().maxScore([1,2,1,2,1], 1) == 3 # optimal to sometimes skip
# Large values
assert Solution().maxScore([10**6]*10**5, 1) == 10**6 * 10**5 # all same parity, no penalty
| Test | Why |
|---|---|
| [2,3,6,1,9,2], x=5 | Tests switching parity and penalty calculation |
| [2,4,6,8], x=3 | Tests all elements same parity, no |