day11: part2
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 5.7 KiB |
@@ -1,6 +1,6 @@
|
|||||||
<!-- AOC TILES BEGIN -->
|
<!-- AOC TILES BEGIN -->
|
||||||
<h1 align="center">
|
<h1 align="center">
|
||||||
2025 - 19 ⭐ - Rust
|
2025 - 20 ⭐ - Rust
|
||||||
</h1>
|
</h1>
|
||||||
<a href="src/day1.rs">
|
<a href="src/day1.rs">
|
||||||
<img src=".aoc_tiles/tiles/2025/01.png" width="161px">
|
<img src=".aoc_tiles/tiles/2025/01.png" width="161px">
|
||||||
|
|||||||
106
src/day11.rs
106
src/day11.rs
@@ -1,27 +1,19 @@
|
|||||||
use aoc_runner_derive::{aoc, aoc_generator};
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
use cached::proc_macro::cached;
|
use std::collections::{HashMap, HashSet};
|
||||||
use indicatif::{ProgressBar, ProgressFinish, ProgressStyle};
|
|
||||||
use itertools::Itertools;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
type Edges = HashMap<String, Vec<String>>;
|
type Edges = HashMap<String, Vec<String>>;
|
||||||
|
|
||||||
#[aoc_generator(day11)]
|
#[aoc_generator(day11)]
|
||||||
fn parse(input: &str) -> (Edges, Edges) {
|
fn parse(input: &str) -> Edges {
|
||||||
let mut edges: Edges = HashMap::new();
|
let mut edges: Edges = HashMap::new();
|
||||||
let mut rev_edges: Edges = HashMap::new();
|
|
||||||
for l in input.lines() {
|
for l in input.lines() {
|
||||||
let (k, rest) = l.split_once(": ").unwrap();
|
let (k, rest) = l.split_once(": ").unwrap();
|
||||||
for v in rest.split_ascii_whitespace() {
|
for v in rest.split_ascii_whitespace() {
|
||||||
edges.entry(k.to_string()).or_default().push(v.to_string());
|
edges.entry(k.to_string()).or_default().push(v.to_string());
|
||||||
rev_edges
|
|
||||||
.entry(v.to_string())
|
|
||||||
.or_default()
|
|
||||||
.push(k.to_string());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(edges, rev_edges)
|
edges
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_path(cur: &str, goal: &str, edges: &Edges) -> u64 {
|
fn find_path(cur: &str, goal: &str, edges: &Edges) -> u64 {
|
||||||
@@ -35,26 +27,72 @@ fn find_path(cur: &str, goal: &str, edges: &Edges) -> u64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mark_paths<'a>(
|
||||||
|
cur: &'a str,
|
||||||
|
edges: &'a Edges,
|
||||||
|
mut reachable: HashSet<&'a str>,
|
||||||
|
) -> HashSet<&'a str> {
|
||||||
|
reachable.insert(cur);
|
||||||
|
if let Some(nexts) = edges.get(cur) {
|
||||||
|
for n in nexts {
|
||||||
|
if !reachable.contains(&n.as_str()) {
|
||||||
|
// reachable.insert(n);
|
||||||
|
reachable = mark_paths(n, edges, reachable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reachable
|
||||||
|
}
|
||||||
|
|
||||||
#[aoc(day11, part1)]
|
#[aoc(day11, part1)]
|
||||||
fn part1((fwd, rev): &(Edges, Edges)) -> u64 {
|
fn part1(fwd: &Edges) -> u64 {
|
||||||
let mut count = 0u64;
|
|
||||||
let mut to_check = Vec::from_iter(rev["out"].iter());
|
|
||||||
|
|
||||||
let mut pb = ProgressBar::no_length()
|
|
||||||
.with_style(
|
|
||||||
ProgressStyle::with_template(
|
|
||||||
"[{elapsed_precise}/{eta_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {per_sec}",
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
.with_finish(indicatif::ProgressFinish::AndLeave);
|
|
||||||
|
|
||||||
find_path("you", "out", fwd)
|
find_path("you", "out", fwd)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day11, part2)]
|
#[aoc(day11, part2)]
|
||||||
fn part2(input: &(Edges, Edges)) -> u64 {
|
fn part2(edges: &Edges) -> u64 {
|
||||||
0
|
let mut rev = Edges::from_iter(edges.keys().map(|k| (k.to_owned(), vec![])));
|
||||||
|
for (from, tos) in edges {
|
||||||
|
for to in tos {
|
||||||
|
rev.entry(to.to_owned()).or_default().push(from.to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut reachable_dac = mark_paths("dac", edges, HashSet::new());
|
||||||
|
reachable_dac = mark_paths("dac", &rev, reachable_dac);
|
||||||
|
let mut reachable_fft = mark_paths("fft", edges, HashSet::new());
|
||||||
|
reachable_fft = mark_paths("fft", &rev, reachable_fft);
|
||||||
|
|
||||||
|
let reachable: HashSet<&str> = reachable_dac
|
||||||
|
.intersection(&reachable_fft)
|
||||||
|
.copied()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let unreachable: HashSet<&str> = HashSet::from_iter(edges.keys().map(|k| k.as_str()))
|
||||||
|
.difference(&reachable)
|
||||||
|
.copied()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut reachable_edges = edges.clone();
|
||||||
|
|
||||||
|
for k in &unreachable {
|
||||||
|
reachable_edges.remove(*k);
|
||||||
|
}
|
||||||
|
for (_k, v) in reachable_edges.iter_mut() {
|
||||||
|
for ur in &unreachable {
|
||||||
|
if let Some(idx) = v.iter().position(|s| s == ur) {
|
||||||
|
v.remove(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a bit of a cheat from viewing the graph and realizing all paths are ordered this way.
|
||||||
|
// However, knowing that fact that we can partition in this way, we could use a shortest-path algo
|
||||||
|
// to determine which order fft and dac are in.
|
||||||
|
//
|
||||||
|
// The assumption holds on my data and the example, so I think it's probably true of all inputs.
|
||||||
|
find_path("svr", "fft", &reachable_edges)
|
||||||
|
* find_path("fft", "dac", &reachable_edges)
|
||||||
|
* find_path("dac", "out", &reachable_edges)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -74,6 +112,20 @@ ggg: out
|
|||||||
hhh: ccc fff iii
|
hhh: ccc fff iii
|
||||||
iii: out";
|
iii: out";
|
||||||
|
|
||||||
|
const EXAMPLE2: &str = "svr: aaa bbb
|
||||||
|
aaa: fft
|
||||||
|
fft: ccc
|
||||||
|
bbb: tty
|
||||||
|
tty: ccc
|
||||||
|
ccc: ddd eee
|
||||||
|
ddd: hub
|
||||||
|
hub: fff
|
||||||
|
eee: dac
|
||||||
|
dac: fff
|
||||||
|
fff: ggg hhh
|
||||||
|
ggg: out
|
||||||
|
hhh: out";
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
#[case(EXAMPLE, 5)]
|
#[case(EXAMPLE, 5)]
|
||||||
fn part1_example(#[case] input: &str, #[case] expected: u64) {
|
fn part1_example(#[case] input: &str, #[case] expected: u64) {
|
||||||
@@ -81,7 +133,7 @@ iii: out";
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
#[case(EXAMPLE, 0)]
|
#[case(EXAMPLE2, 2)]
|
||||||
fn part2_example(#[case] input: &str, #[case] expected: u64) {
|
fn part2_example(#[case] input: &str, #[case] expected: u64) {
|
||||||
assert_eq!(part2(&parse(input)), expected);
|
assert_eq!(part2(&parse(input)), expected);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user