Compare commits

...

4 Commits

View File

@@ -1,21 +1,6 @@
module StringMap = Map.Make (String)
module StringSet = Set.Make (String)
let[@warning "-32"] print_set set =
StringSet.iter
(fun x ->
print_string x;
print_char ' ')
set;
print_newline ()
let[@warning "-32"] print_sets = List.iter print_set
let[@warning "-32"] print_map =
StringMap.iter (fun k v ->
Printf.printf "%s: " k;
print_set v)
let add_connection map (a, b) =
let update s = function
| None -> Some (StringSet.add s StringSet.empty)
@@ -35,37 +20,26 @@ 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
| [] -> acc
| h :: t ->
let rec impl acc set = function
| [] -> acc
| c :: t -> impl (StringSet.add c set :: acc) set t
in
match StringSet.choose_opt candidates with
| None -> acc
| Some h ->
let candidates = StringSet.remove h candidates in
if StringSet.mem h visited then
find_second_member acc connections visited a t
find_second_member acc connections visited a candidates
else
let visited = StringSet.add h visited in
let anh = StringSet.inter (StringMap.find h connections) candidates in
let acc =
find_third_member acc connections visited a h
(StringSet.to_list (StringMap.find h connections))
impl acc (StringSet.of_list [ a; h ]) (StringSet.to_list anh)
in
find_second_member acc connections visited a t
find_second_member acc connections visited a candidates
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
@@ -73,19 +47,61 @@ let rec find_rings acc visited connections computers =
let visited = StringSet.add h visited in
let acc =
find_second_member acc connections visited h
(StringSet.to_list (StringMap.find h connections))
(StringMap.find h connections)
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 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
print_sets rings;
List.length rings
StringMap.to_list connections
|> List.map fst
|> find_rings [] StringSet.empty connections
|> List.filter starts_with_t |> List.length |> string_of_int
let _ = Aoc.main load_file [ (string_of_int, part1) ]
(** [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' = 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_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 =
find_max_set connections |> List.sort compare |> String.concat ","
let _ = Aoc.main load_file [ (Fun.id, part1); (Fun.id, part2) ]