From 2f30285fe7d4aa2a375e983b12d08e96952063b7 Mon Sep 17 00:00:00 2001 From: Matthew Gretton-Dann Date: Mon, 23 Dec 2024 12:45:43 +0000 Subject: [PATCH] 2024 day 23 some further tidy-ups. --- bin/day2423.ml | 100 +++++++++++++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 44 deletions(-) diff --git a/bin/day2423.ml b/bin/day2423.ml index 6ff7ea8..6bfec85 100644 --- a/bin/day2423.ml +++ b/bin/day2423.ml @@ -20,37 +20,27 @@ let load_file fname = |> List.map make_pairs |> List.fold_left add_connection StringMap.empty -let is_three_ring connections a _ candidate = - StringSet.mem candidate (StringMap.find a connections) - -let rec find_third_member acc connections visited a b candidates = - match candidates with - | [] -> acc - | h :: t -> - if StringSet.mem h visited then - find_third_member acc connections visited a b t - else if not (is_three_ring connections a b h) then - find_third_member acc connections visited a b t - else - let acc = StringSet.of_list [ a; b; h ] :: acc in - find_third_member acc connections visited a b t - -let rec find_second_member acc connections visited a candidates = - match candidates with +let rec find_second_member acc connections visited a = + let rec impl acc a b = function + | [] -> acc + | c :: t -> impl (StringSet.of_list [ a; b; c ] :: acc) a b t + in + function | [] -> acc | h :: t -> if StringSet.mem h visited then find_second_member acc connections visited a t else let visited = StringSet.add h visited in - let acc = - find_third_member acc connections visited a h - (StringSet.to_list (StringMap.find h connections)) + let anh = + StringSet.inter + (StringMap.find a connections) + (StringMap.find h connections) in + let acc = impl acc a h (StringSet.to_list anh) in find_second_member acc connections visited a t -let rec find_rings acc visited connections computers = - match computers with +let rec find_rings acc visited connections = function | [] -> acc | h :: t -> if StringSet.mem h visited then find_rings acc visited connections t @@ -62,38 +52,60 @@ let rec find_rings acc visited connections computers = in find_rings acc visited connections t +(** [starts_with_t set] returns true if any member of [set] starts with the + letter ['t']. *) let starts_with_t set = StringSet.exists (fun x -> x.[0] = 't') set +let set_compare a b = compare (StringSet.elements a) (StringSet.elements b) + let part1 connections = - let computers = StringMap.to_list connections |> List.map fst in - let rings = - find_rings [] StringSet.empty connections computers - |> List.filter starts_with_t - in - string_of_int (List.length rings) + StringMap.to_list connections + |> List.map fst + |> find_rings [] StringSet.empty connections + |> List.filter starts_with_t |> List.sort_uniq set_compare |> List.length + |> string_of_int -let rec search_candidate max_set connections current candidates = - match StringSet.choose_opt candidates with - | None -> - if List.length current > List.length max_set then current else max_set - | Some h -> +(** [search_candidate max_lst connections current candidates] searches for the + longest star network that contains all the computers in [current] and maybe + some of the elements of [candidates]. + + [max_lst] is the biggest network found so far. [connections] is the map of + computers to direct connections. + + The initial call should look something like: + + [search_candidate [] connections k (StringSet.to_list (StringMap.find k + connections))] *) +let rec search_candidate max_lst connections current candidates = + match candidates with + | [] -> if List.length current > List.length max_lst then current else max_lst + | h :: t -> + let map = StringMap.find h connections in let current' = h :: current in - let candidates' = - StringSet.inter candidates (StringMap.find h connections) - in - let max_set = search_candidate max_set connections current' candidates' in - search_candidate max_set connections current - (StringSet.remove h candidates) + let candidates' = List.filter (Fun.flip StringSet.mem map) candidates in + let max_lst = search_candidate max_lst connections current' candidates' in + search_candidate max_lst connections current t +(** [find_max_set connections] returns a list containing the largest number of + computers in a star network (that is for every pair of elements in the list + there is a connection between them). + + [connections] is the map of connections keyed by computer with the value + being a set of all direct connections. [connections] must be bi-directional + that is if: [StringSet.mem a (StringMap.find b connections)] then + [StringSet.mem b (StringMap.find a connections)]. Note that + [StringSet.mem a (StringMap.find a connections)] must return [false]. *) let find_max_set connections = - let rec impl max_set = function - | [] -> max_set - | (k, v) :: t -> impl (search_candidate max_set connections [ k ] v) t + let rec impl max_lst = function + | [] -> max_lst + | (k, v) :: t -> + impl + (search_candidate max_lst connections [ k ] (StringSet.to_list v)) + t in impl [] (StringMap.to_list connections) let part2 connections = - let max_set = find_max_set connections in - List.sort compare max_set |> String.concat "," + find_max_set connections |> List.sort compare |> String.concat "," let _ = Aoc.main load_file [ (Fun.id, part1); (Fun.id, part2) ]