Skip to content

Commit b9192b9

Browse files
committed
union-find
1 parent 3daa209 commit b9192b9

File tree

3 files changed

+73
-49
lines changed

3 files changed

+73
-49
lines changed

aoc2025_wasm/src/impl_2025_08.rs

Lines changed: 15 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
use std::cmp::Reverse;
1+
use crate::union_find::UfNode;
22
use std::collections::BinaryHeap;
3-
use std::{cell::RefCell, collections::HashSet, str::FromStr};
3+
use std::str::FromStr;
4+
use std::{cmp::Reverse, hash::Hash};
45

56
use anyhow::{Context, Result, ensure};
67
use itertools::Itertools;
78

8-
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
9+
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
910
struct Coord {
1011
x: i64,
1112
y: i64,
@@ -38,74 +39,39 @@ impl FromStr for Coord {
3839
}
3940
}
4041

41-
fn calculate_p1(circuits: &[RefCell<HashSet<&Coord>>]) -> usize {
42-
circuits
43-
.iter()
44-
.map(|c| c.borrow().len())
45-
.sorted_unstable()
46-
.rev()
47-
.take(3)
48-
.product()
49-
}
50-
5142
fn calculate<const P1_CONNECTIONS: usize>(inp: &str) -> Result<(usize, i64)> {
5243
let coords = inp
5344
.lines()
5445
.map(|line| line.parse::<Coord>())
46+
.zip(0_usize..)
47+
.map(|(c, i)| c.map(|c| (c, i)))
5548
.collect::<Result<Vec<_>>>()?;
5649

5750
let mut distances = coords
5851
.iter()
5952
.tuple_combinations()
60-
.map(|(b1, b2)| (Reverse(b1.dist_squared(b2)), b1, b2))
53+
.map(|((b1, id1), (b2, id2))| (Reverse(b1.dist_squared(b2)), id1, id2, b1, b2))
6154
.collect::<BinaryHeap<_>>();
6255

63-
let mut circuits: Vec<RefCell<HashSet<&Coord>>> = vec![];
56+
let mut circuits: UfNode = UfNode::new((0..coords.len()).collect::<Vec<_>>());
6457
let mut connections = 0;
6558

6659
let mut p1 = 0;
6760
let mut p2 = 0;
6861

69-
while let Some((_, b1, b2)) = distances.pop() {
70-
let mut added = false;
71-
for c in circuits.iter() {
72-
let mut c = c.borrow_mut();
73-
if c.contains(b1) || c.contains(b2) {
74-
c.insert(b1);
75-
c.insert(b2);
76-
for c2 in circuits.iter() {
77-
if let Ok(mut c2) = c2.try_borrow_mut()
78-
&& (c2.contains(b1) || c2.contains(b2))
79-
{
80-
c.extend(c2.iter());
81-
c2.clear();
82-
}
83-
}
84-
added = true;
85-
break;
86-
}
87-
}
88-
89-
if !added {
90-
let new_circuit = HashSet::from([b1, b2]);
91-
circuits.push(RefCell::new(new_circuit));
92-
}
93-
94-
circuits.retain(|c| !c.borrow().is_empty());
62+
while let Some((_, id1, id2, b1, b2)) = distances.pop() {
63+
circuits.union_sets(*id1, *id2);
9564

9665
connections += 1;
9766

9867
if connections == P1_CONNECTIONS {
99-
p1 = calculate_p1(&circuits);
68+
let mut circuit_sizes = circuits.sizes();
69+
let partition_point = circuit_sizes.len();
70+
let (_, _, top3) = circuit_sizes.select_nth_unstable(partition_point.saturating_sub(4));
71+
p1 = top3.iter().product();
10072
}
10173

102-
let max_circuit_length = circuits
103-
.iter()
104-
.map(|c| c.borrow().len())
105-
.max()
106-
.context("no circuits")?;
107-
108-
if max_circuit_length == coords.len() {
74+
if circuits.num_sets() == 1 {
10975
ensure!(p1 != 0, "solved part 2 before part 1?");
11076
p2 = b1.x * b2.x;
11177
break;

aoc2025_wasm/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod impl_2025_05;
77
mod impl_2025_06;
88
mod impl_2025_07;
99
mod impl_2025_08;
10+
pub(crate) mod union_find;
1011

1112
pub use wasm_bindgen_rayon::init_thread_pool;
1213

aoc2025_wasm/src/union_find.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
pub struct UfNode {
2+
items: Vec<usize>,
3+
sizes: Vec<usize>,
4+
num_sets: usize,
5+
}
6+
7+
impl UfNode {
8+
pub fn new(items: Vec<usize>) -> UfNode {
9+
UfNode {
10+
sizes: vec![1; items.len()],
11+
num_sets: items.len(),
12+
items,
13+
}
14+
}
15+
16+
pub fn find_representative(&mut self, elem: usize) -> Option<usize> {
17+
let parent = *self.items.get(elem)?;
18+
19+
if parent == elem {
20+
Some(parent)
21+
} else {
22+
let v = self.find_representative(parent)?;
23+
*self.items.get_mut(elem)? = v;
24+
Some(v)
25+
}
26+
}
27+
28+
pub fn union_sets(&mut self, a: usize, b: usize) -> Option<()> {
29+
let a = self.find_representative(a)?;
30+
let b = self.find_representative(b)?;
31+
32+
if a != b {
33+
let a_size = self.sizes.get(a)?;
34+
let b_size = self.sizes.get(b)?;
35+
36+
if a_size < b_size {
37+
*self.items.get_mut(a)? = b;
38+
*self.sizes.get_mut(b)? = a_size + b_size;
39+
*self.sizes.get_mut(a)? = 0;
40+
} else {
41+
*self.items.get_mut(b)? = a;
42+
*self.sizes.get_mut(a)? = a_size + b_size;
43+
*self.sizes.get_mut(b)? = 0;
44+
}
45+
self.num_sets -= 1;
46+
}
47+
Some(())
48+
}
49+
50+
pub fn num_sets(&self) -> usize {
51+
self.num_sets
52+
}
53+
54+
pub fn sizes(&self) -> Vec<usize> {
55+
self.sizes.iter().copied().filter(|&n| n > 0).collect()
56+
}
57+
}

0 commit comments

Comments
 (0)