diff --git a/2015/day-09/notes.md b/2015/day-09/notes.md index 4144c2c..5718feb 100644 --- a/2015/day-09/notes.md +++ b/2015/day-09/notes.md @@ -13,3 +13,20 @@ Visit all nodes with shortest path 3. Set next node to current node 6. Otherwise: 1. Return with current stack + + +## Search Algorithm 2 (Depth First Search) + +`O(n^2)` +Visit all nodes with longest path + +1. Create a set for visited nodes +2. Begin search at start node +3. Mark current node as visited +4. Iterate through all outgoing nodes that are not marked as visited +5. Push node onto path +6. Add edge weight to local length +7. Recursively repeat steps 3 to 7 with outgoing nodes until all were visited +8. When encountering a visited node compare max distance with local distance + 1. Replace max distance with local distance if max distance is smaller +10. Reset local length for next node (only necessary if not a complete graph) diff --git a/2015/day-09/src/main.rs b/2015/day-09/src/main.rs index 8b0e791..fcabb73 100644 --- a/2015/day-09/src/main.rs +++ b/2015/day-09/src/main.rs @@ -1,6 +1,7 @@ mod graph; mod utils; mod part1; +mod part2; type Graph = graph::Graph; type Node = graph::Node; @@ -27,6 +28,7 @@ fn main() -> Result<(), Box> { utils::write_dot(&directions, file_path)?; println!("\tPart 1: {:?}", part1::solve(&directions)); + println!("\tPart 2: {:?}", part2::solve(&directions)); } Ok(()) diff --git a/2015/day-09/src/part2.rs b/2015/day-09/src/part2.rs new file mode 100644 index 0000000..503968e --- /dev/null +++ b/2015/day-09/src/part2.rs @@ -0,0 +1,74 @@ +use std::collections::HashSet; +use crate::{Distance, Graph, Node}; + +type NodeSet = HashSet; + +struct Memo<'a> { + max_dist: &'a mut Distance, + prev_dist: Distance, + visited: &'a mut NodeSet, + path: &'a mut Vec<(Node, Distance)>, +} + +fn depth_first_search(node: &Node, memo: Memo<'_>) { + memo.visited.insert(node.clone()); + + let mut distance = 0; + + for (out_node, out_weight) in &node.borrow().outgoing { + if !memo.visited.contains(&out_node) { + memo.path.push((out_node.clone(), *out_weight)); + + distance = memo.prev_dist + out_weight; + + depth_first_search(&out_node, Memo { + max_dist: &mut *memo.max_dist, + prev_dist: distance, + visited: &mut *memo.visited, + path: &mut *memo.path + }); + } + + if *memo.max_dist < distance { + *memo.max_dist = distance; + } + + distance = 0 + } +} + +fn visit_all_longest(start_node: &Node) -> Vec<(Node, Distance)> { + let mut visited = NodeSet::new(); + let mut path = Vec::new(); + + depth_first_search(&start_node, Memo { + max_dist: &mut 0, + prev_dist: 0, + visited: &mut visited, + path: &mut path + }); + + path +} + +pub fn solve(directions: &Graph) -> Option { + use std::cmp::max; + + let mut longest_distance = None; + + for node in &directions.nodes { + let path = visit_all_longest(node); + + if path.is_empty() { + continue; + } + + let distance = path.iter().map(|p| p.1).sum(); + longest_distance = match longest_distance { + None => Some(distance), + Some(d) => Some(max(distance, d)), + }; + } + + longest_distance +}