const std = @import("std"); const PATH = "input/day17.txt"; const Str = []const u8; const BUCKETS = std.mem.count(u8, @embedFile(PATH), "\n"); const BucketType = u6; const MaskSize = std.meta.Int(.unsigned, 20); pub fn first(allocator: std.mem.Allocator) !usize { const buckets = try parseInput(allocator, @embedFile(PATH)); defer allocator.free(buckets); var used = std.StaticBitSet(BUCKETS).initEmpty(); var sets = std.ArrayList(MaskSize).init(allocator); defer sets.deinit(); try recurse(buckets, 0, 150, &used, &sets); return sets.items.len; } pub fn second(allocator: std.mem.Allocator) !usize { const buckets = try parseInput(allocator, @embedFile(PATH)); defer allocator.free(buckets); var used = std.StaticBitSet(BUCKETS).initEmpty(); var sets = std.ArrayList(MaskSize).init(allocator); defer sets.deinit(); try recurse(buckets, 0, 150, &used, &sets); var min = std.AutoHashMap(usize, usize).init(allocator); defer min.deinit(); var min_cont: usize = std.math.maxInt(usize); for (sets.items) |item| { const count = @popCount(item); if (count < min_cont) min_cont = count; const res = try min.getOrPut(count); if (res.found_existing) res.value_ptr.* += 1 else res.value_ptr.* = 1; } return min.get(min_cont).?; } test "day17a" { try std.testing.expectEqual(@as(usize, 654), try first(std.testing.allocator)); } test "day17b" { try std.testing.expectEqual(@as(usize, 57), try second(std.testing.allocator)); } fn recurse( buckets: []BucketType, start: usize, liters: usize, used: *std.StaticBitSet(BUCKETS), sets: *std.ArrayList(MaskSize), ) !void { if (liters == 0) { try sets.append(used.mask); return; } var idx: usize = start; while (idx < buckets.len) : (idx += 1) { if (!used.isSet(idx)) { if (buckets[idx] <= liters) { used.set(idx); try recurse(buckets, idx, liters - buckets[idx], used, sets); used.unset(idx); } } } } fn parseInput(allocator: std.mem.Allocator, in: Str) ![]BucketType { var ret = std.ArrayList(BucketType).init(allocator); defer ret.deinit(); var lines = std.mem.tokenize(u8, in, "\n"); while (lines.next()) |line| { try ret.append(try std.fmt.parseUnsigned(BucketType, line, 10)); } return try ret.toOwnedSlice(); } const test_input = \\20 \\15 \\10 \\5 \\5 ;