Spyke

Syndicated from the fediverse. Read and engage on the original instance.

View original on programming.dev
advent_of_codeยทAdvent Of Codebyhades

๐Ÿฆ† Everybody.Codes 2025 Quest 19 Solutions ๐Ÿฆ†

Quest 19: Flappy Quack

  • 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

Link to participate: https://everybody.codes/

View original on programming.dev
8

2 replies

Python

The DP solution to go column by column and compute the min flaps for each cell is correct but too slow. Fortunately this problem can be solved using MATH

# Uses math to calculate the minimum number of flaps needed to reach the last passage at any height.
# This solution relies on a couple of facts:
#   1. The number of flaps needed to reach a passage at (x, y) is ceil((x + y) / 2).
#       Proof:
#       - Each flap increases your height by 1 and each glide decreases it by 1
#       - Suppose you flapped f times and covered a horizontal distance of x
#       - So your final height would be y = f - (x - f) = 2f - x
#       - Now suppose a passage at position p_x begins at height p_y. To enter it, your height must be, y >= p_y
#       - Substituting y, 2f - x >= p_y => 2f - p_x >= p_y
#       - Solving for f, f >= (p_x + p_y) / 2
#       - Since f is an integer, f = ceil((p_x + p_y) / 2)
#   2. For each wall, the lowest opening gives the smallest lower bound on the cumulative number of flaps.
#      The maximum of these bounds is necessary because an earlier high wall may require more flaps than the last wall.
#   3. For this input, that maximum lower bound is attainable through all the walls, so it is the optimal answer.
#      This is not true in general: an opening's upper edge or the distance between walls can make the lowest opening unreachable.
def flapsMath(data: str):
    flaps = 0

    passages = [[int(p) for p in passage.split(',')] for passage in data.splitlines()]
    passages.sort(key=lambda p: (p[0], p[1]))  # Sort passages so that we always have the lowest opening first
    last_seen_x = 0

    for x, y, _ in passages:
        if x != last_seen_x:
            last_seen_x = x
            # calculate the minimum number of flaps needed to cross this passage at its lowest opening
            # (x + y + 1) // 2 is equivalent to ceil((x + y) / 2) for integers
            flaps = max(flaps, (x + y + 1) // 2)

    return flaps
2

Rust

Shortest solution so far.

use std::collections::{BTreeMap};

use interval::{
    IntervalSet,
    ops::Range,
    prelude::{Bounded, Empty, Intersection, Union},
};

pub fn solve_part_1(input: &str) -> String {
    let mut data = BTreeMap::new();
    for v in input.lines().map(|l| {
        l.split(",")
            .map(|v| v.parse().unwrap())
            .collect::<Vec<i64>>()
    }) {
        data.entry(v[0]).or_insert(vec![]).push((v[1], v[2]));
    }
    let mut y_ranges = IntervalSet::new(0, 0);
    let mut x = 0;
    for (wall_x, openings) in data.into_iter() {
        let dx = wall_x - x;
        let mut new_ranges = IntervalSet::empty();
        for interval in y_ranges.into_iter() {
            new_ranges = new_ranges.union(&IntervalSet::new(
                interval.lower() - dx,
                interval.upper() + dx,
            ));
        }
        let mut openings_intervalset = IntervalSet::empty();
        for (opening_start, opening_size) in openings {
            openings_intervalset = openings_intervalset.union(&IntervalSet::new(
                opening_start,
                opening_start + opening_size - 1,
            ));
        }
        y_ranges = new_ranges.intersection(&openings_intervalset);
        x = wall_x;
    }
    let y = y_ranges
        .iter()
        .flat_map(|i| (i.lower()..=i.upper()))
        .find(|y| y % 2 == x % 2)
        .unwrap();
    ((y + x) / 2).to_string()
}

pub fn solve_part_2(input: &str) -> String {
    solve_part_1(input)
}
pub fn solve_part_3(input: &str) -> String {
    solve_part_1(input)
}
2

You reached the end