Visit Array Positions to Maximize Score
MedPrompt
You are given a 0-indexed integer array nums and a positive integer x.
You are initially at position 0 in the array and you can visit other positions according to the following rules:
- If you are currently in position
i, then you can move to any positionjsuch thati < j. - For each position
ithat you visit, you get a score ofnums[i]. - If you move from a position
ito a positionjand the parities ofnums[i]andnums[j]differ, then you lose a score ofx.
Return the maximum total score you can get.
Note that initially you have nums[0] points.
Example 1:
Input: nums = [2,3,6,1,9,2], x = 5
Output: 13
Explanation: We can visit the following positions in the array: 0 -> 2 -> 3 -> 4.
The corresponding values are 2, 6, 1 and 9. Since the integers 6 and 1 have different parities, the move 2 -> 3 will make you lose a score of x = 5.
The total score will be: 2 + 6 + 1 + 9 - 5 = 13.Example 2:
Input: nums = [2,4,6,8], x = 3
Output: 20
Explanation: All the integers in the array have the same parities, so we can visit all of them without losing any score.
The total score is: 2 + 4 + 6 + 8 = 20.
Constraints:
2 <= nums.length <= 1051 <= nums[i], x <= 106
Approaches
2 approaches with complexity analysis and trade-offs.
This approach uses a straightforward dynamic programming solution. We define dp[i] as the maximum score achievable by ending a path of visited positions at index i. To compute dp[i], we consider all possible preceding positions j (where j < i) that we could have jumped from. The score for jumping from j to i is the score of the path ending at j (dp[j]) plus the value nums[i], minus a penalty x if the parities of nums[j] and nums[i] differ. We take the maximum over all possible j's to determine dp[i]. The final answer is the maximum score found in the entire dp array.
Algorithm
- Initialize a
longarraydpof sizen, wherenis the length ofnums.dp[i]will store the maximum score of a path ending at indexi. - Set the base case:
dp[0] = nums[0], as any path must start at index 0. - Iterate with a variable
ifrom 1 ton-1:- For each
i, iterate with a variablejfrom 0 toi-1. - Calculate the score if we jump from a path ending at
jto the element ati. - The score is
dp[j] + nums[i]. A penaltyxis subtracted ifnums[i]andnums[j]have different parities. - Update
dp[i]with the maximum score found among all possible previous positionsj. - The recurrence relation is:
dp[i] = max(dp[i], dp[j] + nums[i] - penalty).
- For each
- After filling the
dparray, the answer is the maximum value withindp, since the path can end at any index.
Walkthrough
We create a DP array, dp, of the same size as nums. dp[i] will store the maximum score of a path ending at index i.
The base case is dp[0] = nums[0], as the path must start at index 0. We initialize all other dp entries to a very small number.
We then iterate from i = 1 to n-1. For each i, we need to find the best previous position j to jump from. We do this by iterating j from 0 to i-1. For each j, we calculate the potential score if we extend the path ending at j by visiting i. This score is dp[j] + nums[i]. If nums[j] and nums[i] have different parities, we subtract the penalty x.
The value of dp[i] is then the maximum of these potential scores over all j < i. The recurrence relation can be expressed as:
dp[i] = max_{0 <= j < i} (dp[j] + nums[i] - ((nums[j] % 2 != nums[i] % 2) ? x : 0))
After the loops complete, the dp array is filled. The maximum score might be achieved by ending at any index, so the final answer is the maximum value in the dp array.
class Solution { public long maxScore(int[] nums, int x) { int n = nums.length; long[] dp = new long[n]; dp[0] = nums[0]; for (int i = 1; i < n; i++) { long maxPrevJumpScore = Long.MIN_VALUE; for (int j = 0; j < i; j++) { long penalty = (nums[i] % 2 != nums[j] % 2) ? x : 0; maxPrevJumpScore = Math.max(maxPrevJumpScore, dp[j] - penalty); } dp[i] = nums[i] + maxPrevJumpScore; } long maxTotalScore = Long.MIN_VALUE; for (long score : dp) { maxTotalScore = Math.max(maxTotalScore, score); } return maxTotalScore; }}Complexity
Time
O(n^2), where n is the number of elements in `nums`. There are two nested loops: the outer loop runs `n-1` times, and the inner loop runs up to `n-1` times for each outer iteration.
Space
O(n), where n is the number of elements in `nums`. This is required for the DP array.
Trade-offs
Pros
The logic is a direct translation of the problem statement into a DP recurrence, making it relatively easy to understand.
It correctly solves the problem for smaller input sizes.
Cons
The time complexity of O(n^2) is too slow for the given constraints (n <= 10^5) and will result in a 'Time Limit Exceeded' error on most platforms.
Uses O(n) space, which might be significant for very large
n.
This approach optimizes the dynamic programming solution by reducing the state. We observe that to calculate the maximum score ending at index i, we only need to know the maximum score of a path ending with an even number and the maximum score of a path ending with an odd number from the previous steps. We don't need to check every single previous index j.
By maintaining just two variables—evenMaxScore and oddMaxScore—we can compute the new maximums in constant time at each step. This eliminates the inner loop of the brute-force approach, reducing the overall time complexity from quadratic to linear.
Algorithm
- Initialize two
longvariables:evenMaxScoreto track the max score ending in an even number, andoddMaxScorefor an odd number. - Handle the first element
nums[0]. Ifnums[0]is even, initializeevenMaxScore = nums[0]andoddMaxScore = nums[0] - x. Ifnums[0]is odd, initializeoddMaxScore = nums[0]andevenMaxScore = nums[0] - x. This setup correctly accounts for the penalty of a hypothetical first move with a parity switch. - Iterate through the array from
i = 1ton-1. - At each element
nums[i]:- If
nums[i]is even, calculate the newevenMaxScore. It's the maximum of jumping from a previous even-ending path (evenMaxScore + nums[i]) or an odd-ending path (oddMaxScore + nums[i] - x). - If
nums[i]is odd, calculate the newoddMaxScoresimilarly. It's the maximum of jumping from an odd-ending path (oddMaxScore + nums[i]) or an even-ending path (evenMaxScore + nums[i] - x).
- If
- After the loop, the maximum possible score is
max(evenMaxScore, oddMaxScore).
Walkthrough
The key insight is that the cost of jumping to nums[i] only depends on the parity of the last element in the path. Therefore, we can optimize the state of our DP. Instead of dp[i], we maintain two variables throughout our iteration:
evenMaxScore: The maximum score of any valid path seen so far that ends with an even number.oddMaxScore: The maximum score of any valid path seen so far that ends with an odd number.
We start by initializing these scores based on nums[0]. Since the path must start at nums[0], if nums[0] is even, evenMaxScore is nums[0]. To handle future calculations, we can set oddMaxScore to nums[0] - x, representing the score if we had to switch parity to start with an odd number. A symmetric initialization is done if nums[0] is odd.
Then, we iterate from i = 1 to n-1. For each nums[i]:
- If
nums[i]is even, the newevenMaxScoreisnums[i]plus the better of two options: coming from a previous even path (evenMaxScore) or coming from a previous odd path with a penalty (oddMaxScore - x). - If
nums[i]is odd, the logic is similar for updatingoddMaxScore.
After iterating through all numbers, the final answer is simply the maximum of evenMaxScore and oddMaxScore.
class Solution { public long maxScore(int[] nums, int x) { long evenMaxScore; long oddMaxScore; // Initialize scores based on the first element if (nums[0] % 2 == 0) { evenMaxScore = nums[0]; oddMaxScore = nums[0] - x; } else { oddMaxScore = nums[0]; evenMaxScore = nums[0] - x; } for (int i = 1; i < nums.length; i++) { int currentNum = nums[i]; if (currentNum % 2 == 0) { // Current number is even evenMaxScore = Math.max(evenMaxScore + currentNum, oddMaxScore - x + currentNum); } else { // Current number is odd oddMaxScore = Math.max(oddMaxScore + currentNum, evenMaxScore - x + currentNum); } } return Math.max(evenMaxScore, oddMaxScore); }}Complexity
Time
O(n), where n is the number of elements in `nums`. We perform a single pass through the array.
Space
O(1), as we only use a constant number of variables regardless of the input size.
Trade-offs
Pros
Extremely efficient with O(n) time complexity, which passes the given constraints.
Uses O(1) space, making it highly memory-efficient.
Cons
The logic, especially the initialization, is more abstract and might be less intuitive than the brute-force approach.
Solutions
Solution
class Solution {public long maxScore(int[] nums, int x) { long[] f = new long[2]; Arrays.fill(f, -(1L << 60)); f[nums[0] & 1] = nums[0]; for (int i = 1; i < nums.length; ++i) { f[nums[i] & 1] = Math.max(f[nums[i] & 1] + nums[i], f[nums[i] & 1 ^ 1] + nums[i] - x); } return Math.max(f[0], f[1]); }}class Solution {public: long long maxScore(vector<int> &nums, int x) { const long long inf = 1LL << 60; vector<long long> f(2, -inf); f[nums[0] & 1] = nums[0]; int n = nums.size(); for (int i = 1; i < n; ++i) { f[nums[i] & 1] = max(f[nums[i] & 1] + nums[i], f[nums[i] & 1 ^ 1] + nums[i] - x); } return max(f[0], f[1]); }};class Solution: def maxScore(self, nums: List[int], x: int) -> int: f = [- inf] * 2 f[nums[0] & 1] = nums[0] for v in nums[1:]: f[v & 1] = max(f[v & 1] + v, f[v & 1 ^ 1] + v - x) return max(f)Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.