Problem 2212

Abstract / Summary

Given a list of scores and a maximum number of arrows, return the highest total score by selecting at most that many scores.

Note

The full problem description, including helpful examples, can be found on LeetCode.

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)
34 lines | 1348 bytes | submitted May 22, 2024 | GitHub   29% comment ratio | tab size 4 | moderate complexity

Difficulty

This problem is ranked as
easy medium hard

and has an acceptance rate of about 52% (40k+ submissions).

Typical patterns to solve this problem:
  • Array
  • Backtracking
  • Bit Manipulation
  • Enumeration
I solved 663 easy, 1250 medium and 309 hard problems

AI Disclaimer

Every single line of code was written by me, 100% from scratch, between August 2023 and August 2024.
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.
LeetCode profile | GitHub repo

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.

written by openai gpt

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.

written by google gemini

Code Quality

Several LLMs ranked my code quality on a scale from 1 (very poor) to 10 (outstanding):
n = 27 | median = 7 | μ = 6.67 | σ = 1.47

Complexity Analysis

not analyzed yet

Other Solutions

I wrote a basic crawler to find other solutions to that problem.
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#
24 links | 11 authors | 8 languages

Videos

5 videos   did my crawler come up with invalid videos ? drop me a line.

Similar Problems

1 similar problem

Solved On The Same Day

3 solutions   May 22, 2024

Extra

There are a few extra pages giving you some insights into my setup:
  • 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