LeetCode 2212 - Maximum Points in an Archery Competition
The problem is asking us to determine the optimal way for Bob to distribute his numArrows across 12 scoring sections (from 0 to 11) in an archery competition in order to maximize his total points against Alice. Alice's arrow distribution is given in aliceArrows.
Difficulty: 🟡 Medium
Topics: Array, Backtracking, Bit Manipulation, Enumeration
Solution
Problem Understanding
The problem is asking us to determine the optimal way for Bob to distribute his numArrows across 12 scoring sections (from 0 to 11) in an archery competition in order to maximize his total points against Alice. Alice's arrow distribution is given in aliceArrows. The scoring rules are such that for each section, the player who has more arrows gets the points corresponding to that section. If both players have the same number of arrows, the points go to Alice. If both have zero arrows, the points are not awarded to anyone.
The input consists of numArrows, an integer indicating how many arrows Bob has, and aliceArrows, an array of 12 integers showing how Alice distributed her arrows across the sections. The output should be an array bobArrows of length 12, showing how Bob should distribute his arrows to maximize his points. The sum of Bob's arrows must equal numArrows.
Key edge cases include scenarios where Bob cannot beat Alice in any section due to insufficient arrows, or when multiple distributions yield the same maximum points. The constraints indicate numArrows can be as high as 10^5, so an efficient solution is necessary.
Approaches
A brute-force approach would consider all possible ways Bob can allocate his numArrows across the 12 sections. For each allocation, we calculate the score and select the allocation with the maximum points. However, the number of allocations grows exponentially with numArrows, making this approach infeasible.
The key observation is that for each section i, Bob only needs to beat Alice by placing at least aliceArrows[i] + 1 arrows in that section. This reduces the problem to a subset selection problem, where we consider all combinations of sections to "win" and compute the corresponding arrow usage and points. Since there are only 12 sections, the total number of subsets is 2^12 = 4096, which is computationally feasible. For each subset, we check if Bob has enough arrows to beat Alice in all chosen sections. We also ensure any leftover arrows are assigned to a section arbitrarily to meet the numArrows constraint.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(numArrows^12) | O(1) | Try all possible arrow distributions; infeasible due to exponential combinations |
| Optimal | O(2^12 * 12) | O(12) | Enumerate all subsets of sections to beat Alice; feasible because 2^12 is small |
Algorithm Walkthrough
- Initialize a variable
maxPointsto 0 to track the highest score Bob can achieve, and a listbestDistributionto store the arrow allocation for that score. - Iterate through all subsets of the 12 sections using a bitmask from 0 to 2^12 - 1. Each bit represents whether Bob tries to win that section.
- For each subset, initialize
arrowsUsedto 0 andpointsto 0. Also initialize a temporary arrow distribution arraytempDistributionof length 12 with zeros. - For each section
i(from 0 to 11), check if the subset includes this section (bit is set). If so, compute the arrows needed to win:aliceArrows[i] + 1. If adding these arrows exceedsnumArrows, break and discard this subset. - If the subset is valid (arrowsUsed <= numArrows), compute the total points for the subset by summing the section indices included. Assign the required arrows in
tempDistributionfor these sections. - If
arrowsUsed < numArrows, assign the remaining arrows arbitrarily to the highest scoring section in the subset to ensure sum equalsnumArrows. - If the total points for this subset are greater than
maxPoints, updatemaxPointsand copytempDistributiontobestDistribution. - After iterating all subsets, return
bestDistribution.
Why it works: The algorithm considers all combinations of sections Bob could try to win. By only considering subsets where Bob beats Alice, we maximize points per arrow usage. The leftover arrows allocation ensures that the solution satisfies the numArrows constraint. Since we evaluate every subset, we are guaranteed to find an optimal solution.
Python Solution
from typing import List
class Solution:
def maximumBobPoints(self, numArrows: int, aliceArrows: List[int]) -> List[int]:
maxPoints = 0
bestDistribution = [0] * 12
for mask in range(1 << 12):
arrowsUsed = 0
points = 0
tempDistribution = [0] * 12
for i in range(12):
if mask & (1 << i):
needed = aliceArrows[i] + 1
arrowsUsed += needed
if arrowsUsed > numArrows:
break
points += i
tempDistribution[i] = needed
else:
if arrowsUsed < numArrows:
for i in reversed(range(12)):
if mask & (1 << i):
tempDistribution[i] += numArrows - arrowsUsed
break
if points > maxPoints:
maxPoints = points
bestDistribution = tempDistribution[:]
return bestDistribution
The implementation iterates through all possible subsets of the 12 sections using a bitmask. For each subset, it calculates the arrows required to win each section and checks if it exceeds numArrows. The total points are computed and, if higher than the previous maximum, the arrow distribution is stored. Any remaining arrows are assigned to the highest section in the subset to meet the total arrow constraint.
Go Solution
func maximumBobPoints(numArrows int, aliceArrows []int) []int {
maxPoints := 0
bestDistribution := make([]int, 12)
for mask := 0; mask < (1 << 12); mask++ {
arrowsUsed := 0
points := 0
tempDistribution := make([]int, 12)
valid := true
for i := 0; i < 12; i++ {
if mask&(1<<i) != 0 {
needed := aliceArrows[i] + 1
arrowsUsed += needed
if arrowsUsed > numArrows {
valid = false
break
}
points += i
tempDistribution[i] = needed
}
}
if valid {
if arrowsUsed < numArrows {
for i := 11; i >= 0; i-- {
if mask&(1<<i) != 0 {
tempDistribution[i] += numArrows - arrowsUsed
break
}
}
}
if points > maxPoints {
maxPoints = points
copy(bestDistribution, tempDistribution)
}
}
}
return bestDistribution
}
The Go version follows the same logic as the Python solution. A key difference is explicit handling of slice copying with copy to avoid reference issues.
Worked Examples
Example: numArrows = 9, aliceArrows = [1,1,0,1,0,0,2,1,0,1,2,0]
| Subset mask | Sections Bob tries | Arrows used | Points | Distribution |
|---|---|---|---|---|
| 101010... | 4,5,8,9,10,11 | 9 | 47 | [0,0,0,0,1,1,0,0,1,2,3,1] |
The algorithm evaluates all 4096 subsets. It finds that selecting sections 4,5,8,9,10,11 yields the maximum points 47, using exactly 9 arrows.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(2^12 * 12) | Enumerates all subsets of 12 sections, checking up to 12 elements per subset |
| Space | O(12) | Stores temporary arrow distributions and the best distribution |
The algorithm is feasible because 2^12 = 4096 subsets, and each subset evaluation is O(12). Space is minimal since only fixed-length arrays are stored.
Test Cases
# test cases
sol = Solution()
# Example 1
assert sum(sol.maximumBobPoints(9, [1,1,0,1,0,0,2,1,0,1,2,0])) == 9 # sum of arrows should equal numArrows
# Example 2
assert sum(sol.maximumBobPoints(3, [0,0,1,0,0,0,0,0,0,0,0,2])) == 3
# Minimum arrows
assert sum(sol.maximumBobPoints(1, [0]*12)) == 1
# Bob cannot beat Alice in any section
assert sum(sol.maximumBobPoints(2, [3]*12)) == 2
# All arrows to highest scoring section
assert sol.maximumBobPoints(5, [0]*12)[11] == 5
| Test | Why |
|---|---|
| Example 1 | Standard problem example, checks correct subset selection |
| Example 2 | Another example, smaller number of arrows |
| Minimum arrows | Edge case with only 1 arrow |
| Bob cannot beat Alice | Edge case |