Python 2212.py
class Solution:
def maximumBobPoints(self, numArrows: int, aliceArrows: List[int]) -> List[int]:
sections = 12
# iterate over all 2^11 bitmasks, ignore section 0
# if a bit is 1, then Bob tries to score one more arrow than Alice
candidates = defaultdict(list)
for bits in range(0, 1 << sections, 2):
score = 0
need = 0 # arrows needed
for i in range(1, sections):
if (1 << i) & bits:
score += i
need += aliceArrows[i] + 1
if need > numArrows:
break
# accept only if enough arrows
if need <= numArrows:
candidates[score].append(bits)
# pick first valid candidate
for score in sorted(candidates, reverse = True):
for bits in candidates[score]:
result = [ 0 ] * sections
for i in range(1, sections):
if (1 << i) & bits:
result[i] = aliceArrows[i] + 1
# throw unused arrows at section 11
result[-1] += numArrows - sum(result)
return result
# zero score, all arrows in section 0
return [ numArrows ] + [ 0 ] * (sections - 1)
Difficulty
and has an acceptance rate of about 52% (40k+ submissions).
Typical patterns to solve this problem:
- Array
- Backtracking
- Bit Manipulation
- Enumeration
AI Disclaimer
However, two years later I set up this website and used various AI tools to generate most of the walkthrough / explanations below.
See this dedicated page for more details.
Code Walkthrough
Overview
The function maximumBobPoints computes a distribution of arrows for Bob that maximizes his score against Alice in a 12-section archery game. The algorithm enumerates possible winning configurations using a bitmask representation, filters those that fit within the available arrow budget, and selects the configuration that yields the highest total score. If no winning configuration is feasible, all arrows are placed in section 0, which yields zero points.
Key Steps
The first step defines the problem size and prepares a container for candidate configurations.
sections = 12
candidates = defaultdict(list)
The core of the algorithm iterates over every bitmask that represents a subset of sections (excluding section 0). The loop steps by two to guarantee that the least-significant bit, which corresponds to section 0, is always zero.
for bits in range(0, 1 << sections, 2):
score = 0
need = 0
for i in range(1, sections):
if (1 << i) & bits:
score += i
need += aliceArrows[i] + 1
if need > numArrows:
break
Inside the inner loop, each set bit indicates that Bob intends to outscore Alice in that section. The required arrows (need) are accumulated as Alice’s arrows in the section plus one extra arrow. The running total score adds the section index, which is the point value for winning that section. An early break aborts the inner loop as soon as the required arrows exceed the budget, preventing unnecessary work for the current bitmask.
After the inner loop finishes, the candidate is stored only if the total arrows needed do not exceed numArrows.
if need <= numArrows:
candidates[score].append(bits)
The next phase selects the highest scoring candidate. The dictionary keys are sorted in descending order, and the first viable bitmask is used to construct the final arrow distribution.
for score in sorted(candidates, reverse = True):
for bits in candidates[score]:
result = [ 0 ] * sections
for i in range(1, sections):
if (1 << i) & bits:
result[i] = aliceArrows[i] + 1
result[-1] += numArrows - sum(result)
return result
If no candidate satisfies the arrow budget, the function falls back to placing all arrows in section 0.
return [ numArrows ] + [ 0 ] * (sections - 1)
Edge Cases Handled
When numArrows is zero, the inner loop never accumulates any required arrows, and the only feasible candidate is the empty bitmask. The final fallback returns a list where the first element is zero and the rest are zeros, correctly representing no arrows placed.
If Alice has already occupied a section with a large number of arrows, the condition need > numArrows triggers an early exit from the inner loop, ensuring that configurations demanding more arrows than available are discarded promptly.
The enumeration skips section 0 by stepping the outer loop with a stride of two, guaranteeing that Bob never attempts to win section 0, which carries no points.
When multiple configurations achieve the same maximal score, the algorithm stores all corresponding bitmasks in candidates[score]. The subsequent nested loops iterate over these bitmasks in insertion order, returning the first one encountered. This deterministic choice respects the problem’s requirement to return any optimal distribution.
The final adjustment result[-1] += numArrows - sum(result) guarantees that any leftover arrows after satisfying the winning sections are placed in the last section (index 11). This ensures the total number of arrows in the result always equals numArrows, even when the exact budget is not consumed by the winning sections.
Performance Optimizations
The use of a bitmask to represent a subset of sections compresses the state space into a single integer, enabling fast bitwise operations for inclusion checks. Enumerating only 2^(sections-1) masks (by skipping the LSB) reduces the search space by half compared with a naïve full enumeration.
The early break inside the inner loop prevents the algorithm from processing the remaining sections once the arrow budget is exceeded, cutting down unnecessary iterations for masks that are already infeasible.
Storing candidates in a dictionary keyed by score allows the algorithm to retrieve the highest scoring configurations without scanning the entire set of masks again. Sorting the keys in descending order provides a direct path to the optimal score.
The construction of the result array reuses the same list template ([0] * sections) and only updates entries for sections that are part of the chosen bitmask, avoiding the need to copy or rebuild the entire list from scratch.
Finally, the immediate return after building the first valid result eliminates further exploration of lower-scoring candidates, ensuring that the function terminates as soon as the optimal solution is found.
ELI5 - Simplified Explanation
Imagine there is a big game with twelve different buckets to throw arrows into, and each bucket has a different number written on it. Alice has already thrown her arrows into these buckets. The goal is to see how many arrows are needed to beat Alice in certain buckets to earn points. Because there are twelve buckets, there are many different combinations of buckets to target. The code creates a list of all possible ways to choose which buckets to win. It checks every single combination to see if there are enough arrows available to beat Alice. After finding all the combinations that work, it looks for the one that earns the most points. Once the best combination is found, it fills in the boxes with the number of arrows needed to win those specific buckets. Any extra arrows left over are simply tossed into the last bucket to make sure they are all used up. Finally, it provides the list showing exactly how to distribute the arrows among the twelve buckets to get the highest score.
Code Quality
Complexity Analysis
Other Solutions
Obviously, I did that after I solved the problem myself.
In general, AlgoMonster and WalkCCC solutions are high-quality and always worth a look.
I haven't checked most of the other solutions, but they might be helpful as well.
And of course, the LeetCode forum is always a good place (or even the best) to look for alternative approaches.
(click on a column header to sort the table)
| author | |||||||||
|---|---|---|---|---|---|---|---|---|---|
| author | solutions | ||||||||
| www | AlgoMonster | C++ | Python | Java | TS | ||||
| WalkCCC | C++ | Python | Java | ||||||
| leetcode.ca | C++ | JS | Python | Java | Go | Rust | TS | ||
| Progiez | C++ | Python | Java | ||||||
| github | black-shadows | C++ | |||||||
| charles-wangkai | Java | ||||||||
| everthis | JS | ||||||||
| fsq | C++ | ||||||||
| JoshCrozier | JS | ||||||||
| lkwq007 | Python | ||||||||
| suxrobGM | C# | ||||||||
| AlgoMonster | C++, Python, Java, TypeScript | ||||||||
| WalkCCC | C++, Python, Java | ||||||||
| leetcode.ca | C++, Go, Java, JavaScript, Python, Rust, TypeScript | ||||||||
| Progiez | C++, Java, Python | ||||||||
| black-shadows | C++, C++ | ||||||||
| charles-wangkai | Java | ||||||||
| everthis | JavaScript | ||||||||
| fsq | C++ | ||||||||
| JoshCrozier | JavaScript | ||||||||
| lkwq007 | Python | ||||||||
| suxrobGM | C# |
Videos
Similar Problems
Solved On The Same Day
- problem 2033: Minimum Operations to Make a Uni-Value Grid
- problem 2191: Sort the Jumbled Numbers
Extra
- Tech Stack - my development environment and tools
- AI - how AI helped me create this website (NOT solving the problems)
- Progress - progression over time
- Quality - code quality