class Solution:
def bagOfTokensScore(self, tokens, power):
tokens.sort()
left, right = 0, len(tokens) - 1
score = 0
max_score = 0
while left <= right:
if power >= tokens[left]:
power -= tokens[left]
score += 1
left += 1
max_score = max(max_score, score)
elif score > 0:
power += tokens[right]
score -= 1
right -= 1
else:
break
return max_score
class Solution {
public:
int bagOfTokensScore(vector<int>& tokens, int power) {
sort(tokens.begin(), tokens.end());
int left = 0, right = tokens.size() - 1;
int score = 0, maxScore = 0;
while (left <= right) {
if (power >= tokens[left]) {
power -= tokens[left++];
score++;
maxScore = max(maxScore, score);
} else if (score > 0) {
power += tokens[right--];
score--;
} else {
break;
}
}
return maxScore;
}
};
import java.util.Arrays;
class Solution {
public int bagOfTokensScore(int[] tokens, int power) {
Arrays.sort(tokens);
int left = 0, right = tokens.length - 1;
int score = 0, maxScore = 0;
while (left <= right) {
if (power >= tokens[left]) {
power -= tokens[left++];
score++;
maxScore = Math.max(maxScore, score);
} else if (score > 0) {
power += tokens[right--];
score--;
} else {
break;
}
}
return maxScore;
}
}
var bagOfTokensScore = function(tokens, power) {
tokens.sort((a, b) => a - b);
let left = 0, right = tokens.length - 1;
let score = 0, maxScore = 0;
while (left <= right) {
if (power >= tokens[left]) {
power -= tokens[left++];
score++;
maxScore = Math.max(maxScore, score);
} else if (score > 0) {
power += tokens[right--];
score--;
} else {
break;
}
}
return maxScore;
};
You are given an array of integers called tokens
and an integer power
. Each token can be played in one of two ways:
power
as the value of a token, you may spend that much power
to gain 1 score (you "play the token face up").power
equal to the value of a token (you "play the token face down").power
.At first glance, it seems we might try every possible order of playing tokens, but that would be very slow (brute-force). Instead, let's think about what maximizes our score:
power
for each point. So, we should use the lowest-value tokens first when playing face up.power
by playing the largest token face down. This gives us the most power
possible for 1 score.power
at the cost of a score.power
if it lets you continue.
We use a greedy, two-pointer strategy:
power
for score, or gain the most power
for a score.left
at the start (smallest token), right
at the end (largest token).power
is at least tokens[left]
, spend tokens[left]
power to gain 1 score, and move left
forward.tokens[right]
power, and move right
backward.
Example Input:
tokens = [100, 200, 300, 400]
, power = 200
Step-by-step:
[100, 200, 300, 400]
power = 200
, score = 0
, max_score = 0
power (200) >= tokens[0] (100)
⇒ play face up, power = 100
, score = 1
, left = 1
power (100) < tokens[1] (200)
⇒ can't play face up. But score > 0
, so play tokens[3] face down: power = 100 + 400 = 500
, score = 0
, right = 2
power (500) >= tokens[1] (200)
⇒ play face up, power = 300
, score = 1
, left = 2
power (300) >= tokens[2] (300)
⇒ play face up, power = 0
, score = 2
, left = 3
left > right
, so we stop.2
.
Brute-force: Trying every permutation of token orders would be O(n!), which is infeasible for large n.
Optimized (Greedy Two-Pointer):
The "Bag of Tokens" problem is best solved using a greedy, two-pointer approach. By always using the smallest tokens to gain score and the largest tokens to regain power (if needed), we maximize our score efficiently. The key insight is to alternate between these two actions, tracking the highest score reached at any point. This strategy is both simple and optimal, making the solution elegant and efficient.