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

  • Pyro@programming.dev
    link
    fedilink
    arrow-up
    1
    ·
    5 days 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