2024 Day 23 Part 2
This commit is contained in:
parent
30f1192f33
commit
45eb3b2cde
2 changed files with 157 additions and 0 deletions
|
|
@ -48,6 +48,12 @@ pub fn AutoHashSet(comptime T: type) type {
|
||||||
self.map.deinit();
|
self.map.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clone(self: Self) !Self {
|
||||||
|
return .{
|
||||||
|
.map = try self.map.clone(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn put(self: *Self, v: T) !void {
|
pub fn put(self: *Self, v: T) !void {
|
||||||
return self.map.put(v, .{});
|
return self.map.put(v, .{});
|
||||||
}
|
}
|
||||||
|
|
@ -66,6 +72,10 @@ pub fn AutoHashSet(comptime T: type) type {
|
||||||
return self.map.count();
|
return self.map.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn empty(self: Self) bool {
|
||||||
|
return self.count() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn pop(self: *Self) ?T {
|
pub fn pop(self: *Self) ?T {
|
||||||
var it = self.iterator();
|
var it = self.iterator();
|
||||||
const key_ptr = it.next() orelse return null;
|
const key_ptr = it.next() orelse return null;
|
||||||
|
|
@ -77,6 +87,38 @@ pub fn AutoHashSet(comptime T: type) type {
|
||||||
pub fn remove(self: *Self, v: T) bool {
|
pub fn remove(self: *Self, v: T) bool {
|
||||||
return self.map.remove(v);
|
return self.map.remove(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn of(allocator: std.mem.Allocator, v: T) !Self {
|
||||||
|
var s = Self.init(allocator);
|
||||||
|
try s.put(v);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with(self: Self, v: T) !Self {
|
||||||
|
var n = try self.clone();
|
||||||
|
try n.put(v);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn @"union"(self: Self, other: Self) !Self {
|
||||||
|
var u = self.clone();
|
||||||
|
var vs = other.iterator();
|
||||||
|
while (vs.next()) |v| {
|
||||||
|
try u.put(v);
|
||||||
|
}
|
||||||
|
return u;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intersect(self: Self, other: Self) !Self {
|
||||||
|
var ix = try self.clone();
|
||||||
|
var vs = self.iterator();
|
||||||
|
while (vs.next()) |v| {
|
||||||
|
if (!other.contains(v.*)) {
|
||||||
|
_ = ix.remove(v.*);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ix;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,11 @@ pub fn main() !void {
|
||||||
|
|
||||||
try stdout.print("{}\n", .{try part1(allocator, edges.items)});
|
try stdout.print("{}\n", .{try part1(allocator, edges.items)});
|
||||||
try bw.flush();
|
try bw.flush();
|
||||||
|
|
||||||
|
var password = try part2(allocator, edges.items);
|
||||||
|
defer password.deinit();
|
||||||
|
try stdout.print("{s}\n", .{password.items});
|
||||||
|
try bw.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
// I really don't want to have to figure out string hashing again, so turn the
|
// I really don't want to have to figure out string hashing again, so turn the
|
||||||
|
|
@ -42,6 +47,13 @@ fn nameStartsWith(n: Name, c: u8) bool {
|
||||||
return n >> 8 == c;
|
return n >> 8 == c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn formatName(n: Name) [2]u8 {
|
||||||
|
return [2]u8{
|
||||||
|
@intCast(n >> 8),
|
||||||
|
@intCast(n & 0xff),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const Edge = struct {
|
const Edge = struct {
|
||||||
a: Name,
|
a: Name,
|
||||||
b: Name,
|
b: Name,
|
||||||
|
|
@ -110,6 +122,28 @@ const Graph = struct {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn popNode(self: *Graph) ?struct { Name, Set(Name) } {
|
||||||
|
const src = self.nodes.pop() orelse return null;
|
||||||
|
return .{ src, self.removeEdgeReferences(src) };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn removeNode(self: *Graph, src: Name) ?Set(Name) {
|
||||||
|
_ = self.nodes.remove(src) or return null;
|
||||||
|
return self.removeEdgeReferences(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn removeEdgeReferences(self: *Graph, src: Name) Set(Name) {
|
||||||
|
var entry = self.edges.fetchRemove(src).?;
|
||||||
|
var it = entry.value.iterator();
|
||||||
|
while (it.next()) |dst| {
|
||||||
|
if (self.edges.getPtr(dst.*)) |set| {
|
||||||
|
_ = set.remove(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry.value;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn part1(allocator: Allocator, edges: []const Edge) !usize {
|
fn part1(allocator: Allocator, edges: []const Edge) !usize {
|
||||||
|
|
@ -139,3 +173,84 @@ fn part1(allocator: Allocator, edges: []const Edge) !usize {
|
||||||
|
|
||||||
return lans.count();
|
return lans.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn part2(allocator: Allocator, edges: []const Edge) !List(u8) {
|
||||||
|
var graph = try Graph.init(allocator, edges);
|
||||||
|
defer graph.deinit();
|
||||||
|
|
||||||
|
var incl = Set(Name).init(allocator);
|
||||||
|
defer incl.deinit();
|
||||||
|
|
||||||
|
var prop = try graph.nodes.clone();
|
||||||
|
defer prop.deinit();
|
||||||
|
|
||||||
|
var excl = Set(Name).init(allocator);
|
||||||
|
defer excl.deinit();
|
||||||
|
|
||||||
|
var lan = Set(Name).init(allocator);
|
||||||
|
try findCliques(graph, incl, prop, excl, &lan);
|
||||||
|
defer lan.deinit();
|
||||||
|
|
||||||
|
return try formatPassword(allocator, lan);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm
|
||||||
|
fn findCliques(graph: Graph, included: Set(Name), proposed: Set(Name), excluded: Set(Name), best: *Set(Name)) !void {
|
||||||
|
if (proposed.empty() and excluded.empty() and included.count() > best.count()) {
|
||||||
|
best.deinit();
|
||||||
|
best.* = try included.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
var p = try proposed.clone();
|
||||||
|
defer p.deinit();
|
||||||
|
|
||||||
|
var x = try excluded.clone();
|
||||||
|
defer x.deinit();
|
||||||
|
|
||||||
|
var it = proposed.iterator();
|
||||||
|
while (it.next()) |v| {
|
||||||
|
const neighbors = graph.edges.get(v.*).?;
|
||||||
|
|
||||||
|
var incl = try included.with(v.*);
|
||||||
|
defer incl.deinit();
|
||||||
|
|
||||||
|
var prop = try p.intersect(neighbors);
|
||||||
|
defer prop.deinit();
|
||||||
|
|
||||||
|
var excl = try x.intersect(neighbors);
|
||||||
|
defer excl.deinit();
|
||||||
|
|
||||||
|
try findCliques(graph, incl, prop, excl, best);
|
||||||
|
|
||||||
|
_ = p.remove(v.*);
|
||||||
|
try x.put(v.*);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn formatPassword(allocator: Allocator, lan: Set(Name)) !List(u8) {
|
||||||
|
var names = List(Name).init(allocator);
|
||||||
|
defer names.deinit();
|
||||||
|
{
|
||||||
|
var it = lan.iterator();
|
||||||
|
while (it.next()) |n| {
|
||||||
|
try names.append(n.*);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std.mem.sort(Name, names.items, {}, comptime std.sort.asc(Name));
|
||||||
|
|
||||||
|
var buf = List(u8).init(allocator);
|
||||||
|
var w = buf.writer();
|
||||||
|
|
||||||
|
if (names.items.len > 0) {
|
||||||
|
try w.print("{s}", .{formatName(names.items[0])});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (names.items.len > 1) {
|
||||||
|
for (names.items[1..]) |n| {
|
||||||
|
try w.print(",{s}", .{formatName(n)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue