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 <= cardPoints.length <= 105
1 <= cardPoints[i] <= 104
1 <= k <= cardPoints.length
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach explores every conceivable combination of taking cards from the beginning or the end of the row. We simulate every possibility to find the best possible score. It's like trying every single hand you could make and then picking the best one.
Here's how the algorithm would work step-by-step:
def max_score_cards_brute_force(card_points, cards_to_take):
max_score = 0
# Check all possibilities by iterating through
# possible combinations of taking from left and right
for num_cards_from_left in range(cards_to_take + 1):
current_score = 0
# Calculate score from the left side
for i in range(num_cards_from_left):
current_score += card_points[i]
# Calculate score from the right side
for i in range(cards_to_take - num_cards_from_left):
current_score += card_points[len(card_points) - 1 - i]
# Maintain the maximum score
max_score = max(max_score, current_score)
# Edge case: If no cards are present, ensure
# the returned score is zero
if not card_points:
return 0
return max_score
The key insight is to focus on what *isn't* being taken rather than what *is*. Instead of trying to pick the best cards from both ends, identify the smallest set of cards to leave behind in the middle, which is much more efficient. We then find the smallest possible sum of cards left behind to get the maximum possible sum of cards we can obtain.
Here's how the algorithm would work step-by-step:
def max_score_sightseeing_pair(card_points, cards_to_take):
total_points = sum(card_points)
# Need to leave this many cards behind
cards_to_leave = len(card_points) - cards_to_take
current_window_sum = sum(card_points[:cards_to_leave])
min_window_sum = current_window_sum
# Slide the window to find the smallest sum
for i in range(cards_to_leave, len(card_points)):
current_window_sum += card_points[i] - card_points[i - cards_to_leave]
min_window_sum = min(min_window_sum, current_window_sum)
# Subtract the minimum we leave to get the maximum
max_obtainable_points = total_points - min_window_sum
return max_obtainable_points
Case | How to Handle |
---|---|
cardPoints is null or empty | Return 0 because there are no cards to pick. |
k is 0 | Return 0 because no cards can be taken. |
k is greater than the length of cardPoints | Return the sum of all elements in cardPoints as we can take all cards. |
cardPoints contains negative numbers | The algorithm should correctly handle negative card points and find the maximum sum, potentially resulting in a negative maximum sum. |
cardPoints contains only zeros | The algorithm should return 0 if k is less than or equal to the length of cardPoints. |
Large array size and large values in cardPoints (potential integer overflow) | Use long instead of int to calculate the sum of card points to prevent integer overflow. |
cardPoints contains extremely skewed distribution (e.g., one very large value, rest are small) | The sliding window/prefix sum approach correctly handles this scenario to find the optimal combination of cards. |
k is equal to the length of the array. | Return the sum of all elements in the array. |