From b4294083a35530316a494e602c26efc6a8adc17c Mon Sep 17 00:00:00 2001 From: Parnic Date: Mon, 23 Dec 2024 16:39:15 -0600 Subject: [PATCH] Day 23 I solved this initially in a way that took around 15 seconds to run (check every possible combination of computers to see which ones were all connected), then did some research and found the "Bron-Kerbosch algorithm" for solving this problem, and wouldn't ya know, a named algorithm solves the problem way faster than brute force. Funny that. --- src/23.cs | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 src/23.cs diff --git a/src/23.cs b/src/23.cs new file mode 100644 index 0000000..b3c634e --- /dev/null +++ b/src/23.cs @@ -0,0 +1,93 @@ +namespace aoc2024; + +internal class Day23 : Day +{ + private readonly Dictionary> connections = []; + + internal override void Parse() + { + var lines = Util.Parsing.ReadAllLines($"{GetDay()}"); + var pairs = lines.Select(line => line.Split('-')); + foreach (var pair in pairs) + { + if (!connections.TryAdd(pair.First(), [pair.Last()])) + { + connections[pair.First()].Add(pair.Last()); + } + + if (!connections.TryAdd(pair.Last(), [pair.First()])) + { + connections[pair.Last()].Add(pair.First()); + } + } + } + + internal override string Part1() + { + HashSet seen = []; + var numGroups = 0; + + foreach (var d1 in connections) + { + foreach (var d2Name in d1.Value) + { + foreach (var d3Name in connections[d2Name]) + { + if (!connections[d3Name].Contains(d1.Key) || + (d1.Key[0] != 't' && d2Name[0] != 't' && d3Name[0] != 't')) + { + continue; + } + + List set = [d1.Key, d2Name, d3Name]; + set.Sort(); + var setStr = string.Join(',', set); + if (!seen.Add(setStr)) + { + continue; + } + + numGroups++; + } + } + } + + return $"# cliques containing a computer starting with 't': <+white>{numGroups}"; + } + + // Bron–Kerbosch algorithm, see https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm + private void BronKerbosch(HashSet r, HashSet p, HashSet x, List> cliques) + { + if (p.Count == 0 && x.Count == 0) + { + cliques.Add(r); + return; + } + + foreach (var v in p.Except(connections[FindNonNeighbor(p, x)])) + { + BronKerbosch([..r, v], [..p.Intersect(connections[v])], [..x.Intersect(connections[v])], cliques); + + p.Remove(v); + x.Add(v); + } + } + + private string FindNonNeighbor(HashSet p, HashSet x) + => p.Union(x).OrderByDescending(v => connections[v].Count).First(); + + private List> GetAllCliques() + { + var cliques = new List>(); + BronKerbosch([], [..connections.Keys], [], cliques); + + return cliques; + } + + internal override string Part2() + { + var cliques = GetAllCliques(); + var password = string.Join(",", cliques.MaxBy(c => c.Count)!.Order()); + return $"LAN party password: <+white>{password}"; + } +}