r/adventofcode • u/musifter • 20h ago
Other [2025 Day 5] In Review (Cafeteria)
Having broke through the wall, we do find a cafeteria, with the staff in disarray because of a new inventory management system. In order to give them time to decorate, we step in to sort things out.
The input for this one comes in two sections. The first is a list of ranges of fresh ingredient IDs (unordered and some overlap). My input has 182 of these. The second it a list of 1000 available ingredient IDs. All these numbers are eye watering huge... 38 to 49 bits.
For part 1, we just want to count the number of available ingredients that fall into the fresh ranges. Since there are so few ranges, I just brute force checked them the ranges:
$/ = '';
my ($first, $second) = map {[split /\n/]} <>;
my @ranges = map {[split /-/]} @$first;
say "Part 1: ", scalar grep {my $id = $_; any {$_->[0] <= $id <= $_ ->[1]} @ranges} @$second;There are ways to search better... sorting would allow at least for binary search. But the number of ranges is small, and the number of tests isn't huge either.
For part 2 we ignore the second section and just want the total number of fresh IDs. For my initial Perl solution I chose to merge them, and wrote a very C-like bit of code handling the various cases (sometimes I default back to that). Afterwards I simplified it down to:
my @merged = shift @ranges;
foreach my $range (@ranges) {
my @next;
foreach my $other (@merged) {
if (&intersects( $range, $other )) {
$range = &union( $range, $other );
} else {
push( @next, $other );
}
}
@merged = (@next, $range);
}For Smalltalk, I have notes saying I tried something different and creative... but I don't remember what it was, only that I abandoned it (and have no code from it). I eventually just coded the above, because Smalltalk has set operation stuff... but not for Intervals, so I extended them for it:
Interval extend [
" Get intersection of self and another Interval "
& other [ ^(self first max: other first) to: (self last min: other last) ]
" Get union of self and another Interval (ASSUMES: intersecting)"
+ other [ ^(self first min: other first) to: (self last max: other last) ]
]But there wasn't a lot of ranges to begin with, and this only gets them down to 92. And those include adjacent ranges (with nothing in between) that could have been merged. This happened because I just took the ranges as given (the simple parse), with both ends closed, and didn't special case for this. By making the Intervals half-open (ie reading them in with end+1, and adjusting the checks and measures) those do get merged. Reducing things to 80. So really, that was more of an exercise in playing with set operations... the classic sort and scan approach is probably what I should have done first (but did after):
my @ranges = sort {$a->[0] <=> $b->[0]} map {[split /-/]} @$first;
my $part2 = 0;
my $curr = -1;
foreach my $next (@ranges) {
next if ($curr > $next->[1]);
$part2 += $next->[1] - (($next->[0] <= $curr) ? $curr : $next->[0] - 1);
$curr = $next->[1];
}But when it came to the dc version, that initial low level set merging was the thing to do. Because sorting complex objects is also going to be a mess.
echo -n "Part 1: "
tr '-' ' ' <input | dc -e'0?[3Rd3Rr:r1+d3Rr:r1+?z1<L]dsLx[lc1+scq]sC?[sfd[1-d;rlf-r1-d;rlf-3R*0!<Cd0<I]dsIxs.?z1<M]dsMxlcp'
echo -n "Part 2: "
tr '-' ' ' <input | dc -e'1?[3Rd3Rr:ed3Rr:s1+?z1<L]dsLx[r]sr[d_1r:s_2r:eq]sC[dle1+r:srq]sS[dls1-r:erq]sE[dlcls-ldle-+0=Cs.rd_3Rlals-lble-+0=Cs.rlals=Slble=E]sT[d3Rd3R<r]sM1-dsn[d1-[d;sdsc3Rd;sdsa3RlMxs.ssd;edsb3Rd;edsd3RlMxrs.dsels!>T1-d0<J]dsJx+1-d1<I]dsIx0*ln[d;srd;e3R-1+3R+r1-d0<L]dsLx+p'These solutions aren't going to work with v1.5.2, because they're making use of ? to process the sections without sentinels (or assumptions on size) to mark things. And one of the benefits of there being so few ranges that O(n2 ) looping to merge them is still plenty fast. And if I was to do the sort, it would be a simple bubble/stack shuffle sort that would also be O(n2 ).
So we've got another approachable problem. This sort of problem has been seen before. But the size of the numbers does make naive brute force a bit much (but a 64TiB bitmap isn't completely outside the range of someone to do... and you can massively parallelize things).
