CF 16B - Burglar and Matches
We have a burglar who can carry exactly n matchboxes. In the warehouse, there are m containers. Each container i has a_i matchboxes, and every matchbox in that container contains b_i matches.
Rating: 900
Tags: greedy, implementation, sortings
Solve time: 1m 13s
Verified: yes
Solution
Problem Understanding
We have a burglar who can carry exactly n matchboxes. In the warehouse, there are m containers. Each container i has a_i matchboxes, and every matchbox in that container contains b_i matches. The goal is to pick up to n matchboxes such that the total number of matches is maximized.
The inputs are two integers n and m followed by m pairs (a_i, b_i). The output is a single integer: the maximum total matches the burglar can take.
The constraints are telling. n can be as large as 2·10^8. This is huge for iterating over individual matchboxes, so any solution that examines each matchbox individually would be too slow. m is at most 20, so the number of containers is tiny. That hints that sorting or a greedy choice over containers is feasible. a_i can be large, so the number of matchboxes in a container can exceed n. b_i is small, 1 to 10, so the matches per box are easy to handle with regular integers.
Edge cases to watch out for include containers that have more matchboxes than the burglar can carry. For example, if n = 5 and a container has a_i = 10 boxes with b_i = 3 matches, the burglar can only take 5 of them. A careless implementation that adds all 10*3 = 30 matches would be wrong. Another edge case is when n is larger than the total number of matchboxes in all containers. In that case, the burglar should just take everything.
Approaches
The brute-force approach would iterate through every possible combination of matchboxes from the containers, summing the total matches and picking the best. With m up to 20, each container having up to 10^8 matchboxes, the number of combinations is astronomical. Even if we only considered containers as whole units, the number of subsets is 2^m = 2^20 ≈ 1 million, which is feasible for subset sums but unnecessary here. Going down to the individual matchboxes is clearly impossible because n can be 2·10^8.
The key observation is that the problem is greedy: to maximize the number of matches, the burglar should first take matchboxes with the largest b_i (matches per box). The order of containers is irrelevant except for b_i. Once containers are sorted by b_i descending, the burglar takes as many boxes as possible from the first container, then the next, until the rucksack is full. This works because each matchbox is indivisible and the boxes with more matches are always strictly better than those with fewer.
| Approach | Time Complexity | Space Complexity | Verdict |
|---|---|---|---|
| Brute Force | O(2^m * m) | O(m) | Too slow |
Greedy / Sort by b_i |
O(m log m) | O(m) | Accepted |
Algorithm Walkthrough
- Read
n(capacity) andm(number of containers). Initializetotal_matches = 0. - Read each container's
(a_i, b_i)and store as a list of tuples. - Sort the list of containers in descending order of
b_i. Sorting ensures we always consider the most valuable boxes first. - Iterate through the sorted containers:
a. Let take = min(a_i, n). This is the number of boxes we can take from this container without exceeding capacity.
b. Add take * b_i to total_matches.
c. Decrease n by take. If n becomes 0, break the loop because the rucksack is full.
5. Print total_matches.
Why it works: At every step, we are taking the available matchboxes with the highest matches per box first. Because matchboxes are indivisible, there is no advantage to leaving a high-value box for a lower-value box. The greedy choice is safe, and by iterating until n boxes are taken, we guarantee that the total matches are maximized.
Python Solution
import sys
input = sys.stdin.readline
n, m = map(int, input().split())
containers = []
for _ in range(m):
a, b = map(int, input().split())
containers.append((b, a)) # store as (matches per box, number of boxes)
# Sort by matches per box descending
containers.sort(reverse=True)
total_matches = 0
for b, a in containers:
if n == 0:
break
take = min(a, n)
total_matches += take * b
n -= take
print(total_matches)
We store (b, a) instead of (a, b) because sorting is easier on b descending. During iteration, take = min(a, n) ensures we never exceed capacity. The loop terminates early if the rucksack is full. These details prevent off-by-one or overflow issues.
Worked Examples
Sample 1:
Input:
7 3
5 10
2 5
3 6
Step-by-step trace:
| Container (b, a) | n before | take | total_matches after | n after |
|---|---|---|---|---|
| (10, 5) | 7 | 5 | 50 | 2 |
| (6, 3) | 2 | 2 | 50 + 12 = 62 | 0 |
| (5, 2) | 0 | 0 | 62 | 0 |
Explanation: First container is richest, take 5 boxes. Next container, only 2 boxes fit in remaining capacity. Total matches = 62.
Another test case:
Input:
10 2
3 10
5 20
| Container (b, a) | n before | take | total_matches after | n after |
|---|---|---|---|---|
| (20, 5) | 10 | 5 | 100 | 5 |
| (10, 3) | 5 | 3 | 100 + 30 = 130 | 2 |
The remaining capacity (2) is more than any remaining boxes, so we stop. Total matches = 130.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(m log m) | Sorting m containers dominates; iteration is O(m) |
| Space | O(m) | Storing the container list |
Given m ≤ 20, sorting is trivial, and the algorithm easily fits within the 0s time limit and 64 MB memory limit. Handling n up to 2·10^8 works because we never iterate per matchbox, only per container.
Test Cases
import sys, io
def run(inp: str) -> str:
sys.stdin = io.StringIO(inp)
import sys
input = sys.stdin.readline
n, m = map(int, input().split())
containers = []
for _ in range(m):
a, b = map(int, input().split())
containers.append((b, a))
containers.sort(reverse=True)
total_matches = 0
for b, a in containers:
if n == 0:
break
take = min(a, n)
total_matches += take * b
n -= take
return str(total_matches)
# Provided sample
assert run("7 3\n5 10\n2 5\n3 6\n") == "62", "sample 1"
# Minimum input
assert run("1 1\n1 1\n") == "1", "minimum input"
# Large n, all containers smaller
assert run("10 2\n3 5\n2 10\n") == "35", "capacity larger than total boxes"
# All boxes same b
assert run("5 2\n3 7\n4 7\n") == "35", "all boxes same value"
# Single rich container exceeds n
assert run("4 1\n10 100\n") == "400", "single container larger than capacity"
| Test input | Expected output | What it validates |
|---|---|---|
| 1 1\n1 1 | 1 | minimum input edge case |
| 10 2\n3 5\n2 10 | 35 | capacity larger than total boxes |
| 5 2\n3 7\n4 7 | 35 | all boxes equal value |
| 4 1\n10 100 | 400 | container larger than capacity, only take n boxes |
Edge Cases
Case where the richest container has more boxes than n:
n = 4, m = 1
10 100
take = min(10, 4) = 4, total_matches = 4*100 = 400. Correct, no overcounting.
Case where total boxes < n:
n = 10, m = 2
3 5
2