|
1 | | -use std::cmp::Reverse; |
| 1 | +use crate::union_find::UfNode; |
2 | 2 | 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}; |
4 | 5 |
|
5 | 6 | use anyhow::{Context, Result, ensure}; |
6 | 7 | use itertools::Itertools; |
7 | 8 |
|
8 | | -#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] |
| 9 | +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] |
9 | 10 | struct Coord { |
10 | 11 | x: i64, |
11 | 12 | y: i64, |
@@ -38,74 +39,39 @@ impl FromStr for Coord { |
38 | 39 | } |
39 | 40 | } |
40 | 41 |
|
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 | | - |
51 | 42 | fn calculate<const P1_CONNECTIONS: usize>(inp: &str) -> Result<(usize, i64)> { |
52 | 43 | let coords = inp |
53 | 44 | .lines() |
54 | 45 | .map(|line| line.parse::<Coord>()) |
| 46 | + .zip(0_usize..) |
| 47 | + .map(|(c, i)| c.map(|c| (c, i))) |
55 | 48 | .collect::<Result<Vec<_>>>()?; |
56 | 49 |
|
57 | 50 | let mut distances = coords |
58 | 51 | .iter() |
59 | 52 | .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)) |
61 | 54 | .collect::<BinaryHeap<_>>(); |
62 | 55 |
|
63 | | - let mut circuits: Vec<RefCell<HashSet<&Coord>>> = vec![]; |
| 56 | + let mut circuits: UfNode = UfNode::new((0..coords.len()).collect::<Vec<_>>()); |
64 | 57 | let mut connections = 0; |
65 | 58 |
|
66 | 59 | let mut p1 = 0; |
67 | 60 | let mut p2 = 0; |
68 | 61 |
|
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); |
95 | 64 |
|
96 | 65 | connections += 1; |
97 | 66 |
|
98 | 67 | 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(); |
100 | 72 | } |
101 | 73 |
|
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 { |
109 | 75 | ensure!(p1 != 0, "solved part 2 before part 1?"); |
110 | 76 | p2 = b1.x * b2.x; |
111 | 77 | break; |
|
0 commit comments