Advent of Code 2025 - Day 5: Cafeteria
Day 5 was about checking ingredient freshness using ID ranges. Part 1 was straightforward with a range check. Part 2 required merging overlapping ranges - something I knew how to do conceptually but struggled to implement correctly.
Part 1: Check Individual IDs
Given a list of fresh ingredient ID ranges and a list of ingredient IDs to check, count how many IDs fall within any range.
The Brute Force Trap
My first instinct was to build a complete set of all fresh IDs:
|
|
My computer killed the program after a couple of minutes. When ranges span millions of IDs, materializing every single number is a disaster.
The Simple Solution
Instead of building ranges, just check if each ingredient falls within any range:
|
|
The key: start <= f <= end is much faster than building massive lists. First try.
Part 2: Merge Overlapping Ranges
Part 2 asks: how many total IDs are considered fresh across all ranges? The ingredient list is now irrelevant - we just need to count unique IDs across potentially overlapping ranges.
The example:
|
|
Ranges 10-14, 16-20, and 12-18 overlap. They should merge into 10-20.
Failed Attempt 1: Materialize Everything
|
|
Terminal force quit again. With ranges spanning billions of IDs, this approach is hopeless.
Failed Attempt 2: In-Place Modification
Sort the ranges, then merge overlapping ones by modifying the list as we iterate:
|
|
Guess was too high. The problem: modifying total_ranges while iterating made the logic confusing. I was trying to track which range was “not merged” but lost track of the state.
Failed Attempt 3: Merged Flag
|
|
Still too high. The flag tracking got messy.
Failed Attempt 4: Don’t Modify Original
|
|
Too low. The bug: I wasn’t reusing the merged range. When ranges A, B, and C all overlap, I needed to merge A+B, then merge that result with C. But I was always comparing against the original ranges from total_ranges.
The Working Solution
The fix: track the “current merged range” and compare each new range against it:
|
|
The key insight: maintain c as the “current range being built.” When the next range overlaps, extend c. When it doesn’t overlap, save c and start fresh with the new range.
Third try in ~1ms.
The Algorithm
Range merging:
- Sort ranges by start position - this ensures we process them left-to-right
- Initialize current range with the first range
- For each subsequent range:
- If it overlaps with current (
c[1] >= n[0]), extend current to include it - If no overlap, save current and start a new current range
- If it overlaps with current (
- Don’t forget to save the final range
Overlap check: ranges [a, b] and [c, d] overlap if b >= c (assuming sorted by start).
Performance
Part 1 brute force (building all IDs): killed after minutes
Part 1 simple check: ~100ms
Part 2 merging ranges: ~1ms
The lesson: think about data structure size. When ranges span billions of values, don’t materialize them. Work with the ranges themselves.
Ten stars total. Part 2 took several attempts to get the merge logic right, but that’s what makes it satisfying.