1423. Maximum Points You Can Obtain From Cards - Explanation

Problem Link

Description

There are several cards arranged in a row, and each card has an associated number of points. The points are given in the integer array cardPoints.

In one step, you can take one card from the beginning or from the end of the row. You have to take exactly k cards.

Your score is the sum of the points of the cards you have taken.

Given the integer array cardPoints and the integer k, return the maximum score you can obtain.

Example 1:

Input: cardPoints = [1,2,3,4,5,6,1], k = 3

Output: 12

Explanation: After the first step, your score will always be 1. However, choosing the rightmost card first will maximize your total score. The optimal strategy is to take the three cards on the right, giving a final score of 1 + 6 + 5 = 12.

Example 2:

Input: cardPoints = [2,2,2], k = 2

Output: 4

Explanation: Regardless of which two cards you take, your score will always be 4.

Example 3:

Input: cardPoints = [9,7,7,9,7,7,9], k = 7

Output: 55

Explanation: You have to take all the cards. Your score is the sum of points of all cards.

Constraints:

  • 1 <= k <= cardPoints.length <= 100,000
  • 1 <= cardPoints[i] <= 10,000


Topics

Company Tags

Please upgrade to NeetCode Pro to view company tags.



Prerequisites

Before attempting this problem, you should be comfortable with:

  • Sliding Window Technique - The key insight is that picking k cards from ends leaves a contiguous window of n-k cards
  • Prefix and Suffix Sums - Used to compute sums of left and right segments in constant time
  • Two Pointers - The optimal solution slides a virtual window that wraps around the array ends

1. Brute Force

Intuition

Since we can only take cards from the beginning or end, any valid selection forms two contiguous segments: one from the left and one from the right, totaling k cards. We can try all possible splits: take 0 from left and k from right, take 1 from left and k-1 from right, and so on.

Algorithm

  1. For each split left from 0 to k:
    • Compute the sum of the first left cards.
    • Compute the sum of the last k - left cards.
    • Track the maximum total.
  2. Return the maximum sum found.
class Solution:
    def maxScore(self, cardPoints: List[int], k: int) -> int:
        n = len(cardPoints)
        res = 0

        for left in range(k + 1):
            leftSum = sum(cardPoints[:left])
            rightSum = sum(cardPoints[n - (k - left):])
            res = max(res, leftSum + rightSum)

        return res

Time & Space Complexity

  • Time complexity: O(k2)O(k ^ 2)
  • Space complexity: O(1)O(1) extra space.

Where kk is the number of cards to pick.


2. Prefix & Suffix Sums

Intuition

Instead of recomputing sums for each split, we precompute prefix sums (sums from the left) and suffix sums (sums from the right). Then for any split, we can get the total in constant time by combining the appropriate prefix and suffix values.

Algorithm

  1. Build a prefix sum array where prefix[i] is the sum of the first i cards.
  2. Build a suffix sum array where suffix[i] is the sum of cards from index i to the end.
  3. For each split taking left cards from the front and k - left from the back:
    • Total = prefix[left] + suffix[n - (k - left)].
    • Track the maximum.
  4. Return the maximum sum.
class Solution:
    def maxScore(self, cardPoints: List[int], k: int) -> int:
        n = len(cardPoints)

        prefix = [0] * (n + 1)
        for i in range(n):
            prefix[i + 1] = prefix[i] + cardPoints[i]

        suffix = [0] * (n + 1)
        for i in range(n - 1, -1, -1):
            suffix[i] = suffix[i + 1] + cardPoints[i]

        res = 0
        for left in range(k + 1):
            right = k - left
            res = max(res, prefix[left] + suffix[n - right])

        return res

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(n)O(n)

3. Sliding Window (Minimum Sum Window)

Intuition

If we pick k cards total from the ends, we leave behind a contiguous subarray of size n - k in the middle. Maximizing the sum of picked cards is equivalent to minimizing the sum of the remaining window. We slide a window of size n - k across the array and find its minimum sum.

Algorithm

  1. Calculate the window size as n - k.
  2. If the window size is 0, return the total sum of all cards.
  3. Slide a window of this size across the array, tracking the minimum window sum.
  4. The answer is the total sum minus this minimum window sum.
class Solution:
    def maxScore(self, cardPoints: List[int], k: int) -> int:
        n = len(cardPoints)
        windowSize = n - k

        if windowSize == 0:
            return sum(cardPoints)

        total = 0
        minWindowSum = float("inf")
        curSum = 0

        for i in range(n):
            total += cardPoints[i]
            curSum += cardPoints[i]
            if i >= windowSize - 1:
                minWindowSum = min(minWindowSum, curSum)
                curSum -= cardPoints[i - windowSize + 1]

        return total - minWindowSum

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(1)O(1) extra space.

4. Sliding Window

Intuition

We can directly maintain a sliding window of size k that wraps around the array. Start by taking all k cards from the right end. Then slide the window: remove one card from the right and add one from the left, tracking the maximum sum as we go.

Algorithm

  1. Initialize the sum by taking the last k cards (all from the right).
  2. Use two pointers: l starting at 0, r starting at n - k.
  3. Slide the window k times:
    • Add cardPoints[l] and subtract cardPoints[r].
    • Update the maximum sum.
    • Move both pointers forward.
  4. Return the maximum sum found.
class Solution:
    def maxScore(self, cardPoints: List[int], k: int) -> int:
        l, r = 0, len(cardPoints) - k
        total = sum(cardPoints[r:])
        res = total

        while r < len(cardPoints):
            total += cardPoints[l] - cardPoints[r]
            res = max(res, total)
            l += 1
            r += 1

        return res

Time & Space Complexity

  • Time complexity: O(k)O(k)
  • Space complexity: O(1)O(1) extra space.

Where kk is the number of cards to pick.


Common Pitfalls

Treating This as a Standard Two-Pointer Problem

Unlike typical two-pointer problems where pointers move toward each other, this problem requires taking cards from both ends that together form a contiguous "virtual" window. The cards you pick are not contiguous in the original array, which confuses many solvers into incorrect pointer movement logic.

Off-by-One Errors in Window Boundaries

When using the sliding window approach (finding minimum sum of n - k elements), incorrect boundary calculations are common. The window size is n - k, not k. Additionally, when indexing the right portion of the array, using n - right instead of the correct index offset leads to accessing wrong elements.

Forgetting the Edge Case When k Equals n

When k equals the array length, you must take all cards. The sliding window approach would have a window of size 0, which needs special handling to avoid division by zero or invalid array access. Simply return the total sum when k == n.