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

  • GiantTree@feddit.org
    link
    fedilink
    arrow-up
    2
    ·
    edit-2
    2日前

    Kotlin

    I like this one. The first part could be solved trivially with a naive approach. The second part is also not too complicated.
    I started out with a simple merging algorithm that would check all ranges, except the one to merge, for overlaps.
    With clever sorting, I can skip the search for mergable ranges and just iterate in reverse and build larger and larger ranges.
    I haven’t tried to merge adjacent ranges like 1…2 and 3…4 to 1…4. The solution is fast enough already when JIT/C2 compiled (200-250 µs).

    Code on Github

    Code
    class Day05 : AOCSolution {
        override val year = 2025
        override val day = 5
    
        override fun part1(inputFile: String): String {
            val (freshIngredients, availableIngredients) = readResourceTwoParts(inputFile)
    
            val freshRanges = buildRanges(freshIngredients)
    
            val ingredientIds = availableIngredients.lines().filterNot(String::isEmpty).map(String::toLong)
    
            val count = ingredientIds.count { id -> freshRanges.any { range -> id in range } }
    
            return count.toString()
        }
    
        override fun part2(inputFile: String): String {
            val (freshIngredients, _) = readResourceTwoParts(inputFile)
    
            val freshRanges = buildRanges(freshIngredients)
    
            return freshRanges.sumOf { range ->
                range.last - range.first + 1
            }.toString()
        }
    
        private companion object {
            private fun buildRanges(ingredients: String): List<LongRange> {
                val lines = ingredients.lines()
                val ranges = MutableList(lines.size) { i ->
                    val line = lines[i]
                    val hyphen = line.indexOf('-')
                    val lower = line.take(hyphen).toLong()
                    val upper = line.substring(hyphen + 1).toLong()
                    LongRange(lower, upper)
                }
    
                // Sort the ranges
                // The ones with the smallest ID and the least upper end
                // get sorted to the beginning.
                // This allows for easy merging, as overlapping ranges are always adjacent
                ranges.sortWith(Comparator { r1, r2 ->
                    val first = r1.first.compareTo(r2.first)
                    if (first != 0) {
                        first
                    } else {
                        r1.last.compareTo(r2.last)
                    }
                })
    
                // Merge adjacent ranges backwards, modifying the list in-place
                for (i in ranges.lastIndex downTo 1) {
                    val lowerRange = ranges[i - 1]
                    val upperRange = ranges[i]
    
                    // The two ranges do overlap, because the tail of the first range
                    // is in the second range.
                    if (upperRange.first <= lowerRange.last) {
                        ranges[i - 1] = LongRange(
                            lowerRange.first,
                            maxOf(lowerRange.last, upperRange.last)
                        )
                        ranges.removeAt(i)
                    }
                }
    
                return ranges
            }
        }
    }