948. Bag of Tokens - Explanation
Prerequisites
Before attempting this problem, you should be comfortable with:
- Sorting - The greedy approach requires sorting tokens to access smallest and largest values efficiently
- Two Pointers - Used to track the smallest (left) and largest (right) tokens simultaneously
- Greedy Algorithms - Understanding when local optimal choices lead to global optimal solutions
1. Greedy + Two Pointers
Intuition
To maximize our score, we should be strategic about which tokens we play face-up (losing power, gaining score) versus face-down (gaining power, losing score). The key insight is that when gaining score, we want to spend as little power as possible, and when gaining power, we want to gain as much as possible. Sorting the tokens lets us always play the smallest token face-up and the largest token face-down.
Algorithm
- Sort the tokens in ascending order.
- Use two pointers:
lstarts at the beginning,rat the end. - While
l <= r:- If we have enough power to play
tokens[l]face-up, do it (gain1score, lose that power). Update the maximum score seen. - Else if we have at least
1score, playtokens[r]face-down (lose1score, gain that power). - Otherwise, we can't make any more moves, so break.
- If we have enough power to play
- Return the maximum score achieved.
class Solution:
def bagOfTokensScore(self, tokens: List[int], power: int) -> int:
res = score = 0
tokens.sort()
l, r = 0, len(tokens) - 1
while l <= r:
if power >= tokens[l]:
power -= tokens[l]
l += 1
score += 1
res = max(res, score)
elif score > 0:
power += tokens[r]
r -= 1
score -= 1
else:
break
return resTime & Space Complexity
- Time complexity:
- Space complexity: or depending on the sorting algorithm.
Common Pitfalls
Forgetting to Sort the Tokens
The greedy approach only works on sorted tokens. Without sorting, you cannot guarantee playing the smallest token face-up and largest face-down.
# Wrong: using unsorted tokens
l, r = 0, len(tokens) - 1
# Right: sort first
tokens.sort()
l, r = 0, len(tokens) - 1Playing Face-Down with Zero Score
You cannot play a token face-down to gain power unless you have at least 1 score to spend. Attempting this leads to incorrect results.
# Wrong: no score check before face-down play
else:
power += tokens[r]
r -= 1
score -= 1
# Right: require score > 0
elif score > 0:
power += tokens[r]
r -= 1
score -= 1Returning Current Score Instead of Maximum
The score fluctuates as you play tokens face-down. You must track the maximum score achieved, not just the final score.
# Wrong: returning final score
return score
# Right: track and return maximum
res = max(res, score) # update after each face-up play
return res