Day 8
This commit is contained in:
parent
47c7362687
commit
aade7c356e
1 changed files with 183 additions and 0 deletions
183
src/bin/day8.rs
Normal file
183
src/bin/day8.rs
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
use std::time::Instant;
|
||||
|
||||
use aoc_2025::{load, print_res};
|
||||
use bstr::BString;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Point3 {
|
||||
pub x: u64,
|
||||
pub y: u64,
|
||||
pub z: u64,
|
||||
}
|
||||
|
||||
impl Point3 {
|
||||
pub fn dist2(&self, other: &Point3) -> u64 {
|
||||
self.x.abs_diff(other.x).pow(2)
|
||||
+ self.y.abs_diff(other.y).pow(2)
|
||||
+ self.z.abs_diff(other.z).pow(2)
|
||||
}
|
||||
}
|
||||
|
||||
type Parsed = Vec<Point3>;
|
||||
|
||||
#[inline(never)]
|
||||
pub fn parsing(input: &BString) -> color_eyre::Result<Parsed> {
|
||||
str::from_utf8(input)?
|
||||
.lines()
|
||||
.map(|v| {
|
||||
let Some((x, r)) = v.split_once(',') else {
|
||||
color_eyre::eyre::bail!("Invalid coordinates: {v}");
|
||||
};
|
||||
let Some((y, z)) = r.split_once(',') else {
|
||||
color_eyre::eyre::bail!("Invalid coordinates: {v}");
|
||||
};
|
||||
|
||||
Ok(Point3 {
|
||||
x: x.parse()?,
|
||||
y: y.parse()?,
|
||||
z: z.parse()?,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct UnionFindEntry {
|
||||
parent: u16,
|
||||
size: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct UnionFind {
|
||||
entries: Vec<UnionFindEntry>,
|
||||
set_count: usize,
|
||||
}
|
||||
|
||||
impl UnionFind {
|
||||
pub fn new(size: usize) -> Self {
|
||||
assert!(size <= u16::MAX as usize);
|
||||
let mut entries = Vec::with_capacity(size);
|
||||
for i in 0..size as u16 {
|
||||
entries.push(UnionFindEntry { parent: i, size: 1 });
|
||||
}
|
||||
|
||||
Self {
|
||||
entries,
|
||||
set_count: size,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find(&mut self, mut x: u16) -> u16 {
|
||||
if self.entries[x as usize].parent != x {
|
||||
let mut root = x;
|
||||
|
||||
while self.entries[root as usize].parent != root {
|
||||
root = self.entries[root as usize].parent;
|
||||
}
|
||||
|
||||
while self.entries[x as usize].parent != root {
|
||||
let parent = self.entries[x as usize].parent;
|
||||
self.entries[x as usize].parent = root;
|
||||
x = parent;
|
||||
}
|
||||
|
||||
root
|
||||
} else {
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
pub fn union(&mut self, a: u16, b: u16) {
|
||||
let mut ra = self.find(a);
|
||||
let mut rb = self.find(b);
|
||||
|
||||
if ra == rb {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.entries[ra as usize].size < self.entries[rb as usize].size {
|
||||
std::mem::swap(&mut ra, &mut rb);
|
||||
}
|
||||
|
||||
self.entries[rb as usize].parent = ra;
|
||||
self.entries[ra as usize].size += self.entries[rb as usize].size;
|
||||
self.set_count -= 1;
|
||||
}
|
||||
|
||||
pub fn sets(&self) -> impl Iterator<Item = (u16, usize)> {
|
||||
self.entries.iter().enumerate().filter_map(|(i, e)| {
|
||||
if e.parent as usize == i {
|
||||
Some((e.parent, e.size as usize))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_count(&self) -> usize {
|
||||
self.set_count
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn part1(input: Parsed) {
|
||||
let mut pairs: Vec<_> = (0..input.len())
|
||||
.flat_map(|ia| ((ia + 1)..input.len()).map(move |ib| (ia, ib)))
|
||||
.collect();
|
||||
pairs.sort_unstable_by_key(|&(ia, ib)| input[ia].dist2(&input[ib]));
|
||||
|
||||
let mut uf = UnionFind::new(input.len());
|
||||
for &(ia, ib) in pairs.iter().take(1000) {
|
||||
uf.union(ia as u16, ib as u16);
|
||||
}
|
||||
|
||||
let mut sets: Vec<_> = uf.sets().collect();
|
||||
sets.sort_unstable_by_key(|&(_, s)| s);
|
||||
|
||||
let mut largests = 1;
|
||||
for _ in 0..3 {
|
||||
largests *= sets.pop().unwrap().1;
|
||||
}
|
||||
|
||||
print_res!("Size product is: {largests}");
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn part2(input: Parsed) {
|
||||
let mut pairs: Vec<_> = (0..input.len())
|
||||
.flat_map(|ia| ((ia + 1)..input.len()).map(move |ib| (ia, ib)))
|
||||
.collect();
|
||||
pairs.sort_unstable_by_key(|&(ia, ib)| input[ia].dist2(&input[ib]));
|
||||
|
||||
let mut uf = UnionFind::new(input.len());
|
||||
for &(ia, ib) in &pairs {
|
||||
uf.union(ia as u16, ib as u16);
|
||||
if uf.set_count() == 1 {
|
||||
print_res!("X product of last boxes: {}", input[ia].x * input[ib].x);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
panic!("Not all boxes have been connected");
|
||||
}
|
||||
|
||||
pub fn main() -> color_eyre::Result<()> {
|
||||
let context = load()?;
|
||||
|
||||
let start = Instant::now();
|
||||
let parsed = parsing(&context.input)?;
|
||||
let elapsed = humantime::format_duration(start.elapsed());
|
||||
|
||||
let start = Instant::now();
|
||||
if context.part == 1 {
|
||||
part1(parsed);
|
||||
} else {
|
||||
part2(parsed);
|
||||
}
|
||||
let elapsed_part = humantime::format_duration(start.elapsed());
|
||||
|
||||
println!(" Parsing: {elapsed}");
|
||||
println!(" Solving: {elapsed_part}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue