Day 5: Cafeteria

Megathread guidelines

  • Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
  • You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL

FAQ

you are viewing a single comment's thread
view the rest of the comments
[โ€“] 2 points 7 months ago

Python

Not too bad though part2 could become difficult if you don't realize the simple method to merge overlapping intervals.

see code

# takes a list of (start, end) ranges and merges overlapping ones
def merge_overlapping_ranges(ranges: list[tuple[int, int]]) -> list[tuple[int, int]]:
    # sort ranges by start so that overlaps are adjacent
    ranges.sort(key=lambda r: r[0])
    merged_ranges = []

    # keep track of a current range that could be merged into
    curr_start, curr_end = ranges[0]
    for start, end in ranges:
        if curr_start <= start <= curr_end:
            # overlap, extend current range
            curr_end = max(curr_end, end)
        else:
            # no overlap, save current range and record the new one
            merged_ranges.append((curr_start, curr_end))
            curr_start, curr_end = start, end
    # finally, don't forget to add the last range that's being tracked
    merged_ranges.append((curr_start, curr_end))

    return merged_ranges

def part1(data: str):
    # split input
    freshness_data, ingredients_data = data.split("\n\n")

    # parse data
    freshness_ranges = [tuple(map(int, f.split('-'))) for f in freshness_data.splitlines()]
    freshness_ranges = merge_overlapping_ranges(freshness_ranges)   # optimization: merge overlapping ranges
    ingredients = [int(i) for i in ingredients_data.splitlines()]

    # checks if an ingredient is fresh
    # this could've been done with binary search, but linear search is simpler and fast enough
    def is_fresh(ingredient: int) -> bool:
        for start, end in freshness_ranges:
            if start <= ingredient <= end:
                # found
                return True
            if start > ingredient:
                # quit early since we are past any range that could've contained the ingredient
                return False
        return False

    # count all fresh ingredients
    return sum(is_fresh(i) for i in ingredients)

def part2(data: str):
    # split input
    freshness_data, _ = data.split("\n\n")

    # parse data
    freshness_ranges = [tuple(map(int, f.split('-'))) for f in freshness_data.splitlines()]
    freshness_ranges = merge_overlapping_ranges(freshness_ranges)   # merge overlapping ranges

    # return total count of fresh ingredients
    # we add +1 because both start and end are inclusive
    return sum(end - start + 1 for start, end in freshness_ranges)

sample = """3-5
10-14
16-20
12-18

1
5
8
11
17
32"""
assert part1(sample) == 3
assert part2(sample) == 14

  • source