Files
grokking_algorithms/10_greedy_algorithms/zig/set_covering.zig
2023-08-09 08:20:19 -05:00

159 lines
4.9 KiB
Zig

const std = @import("std");
const heap = std.heap;
const mem = std.mem;
pub fn main() !void {
var gpa = heap.GeneralPurposeAllocator(.{}){};
var arena = heap.ArenaAllocator.init(gpa.allocator());
defer arena.deinit();
var states_needed_array = [_][]const u8{ "mt", "wa", "or", "id", "nv", "ut", "ca", "az" };
var states_needed = std.BufSet.init(arena.allocator());
for (states_needed_array) |sn| {
try states_needed.insert(sn);
}
var stations = std.StringHashMap(*std.BufSet).init(arena.allocator());
var kone = std.BufSet.init(arena.allocator());
try kone.insert("id");
try kone.insert("nv");
try kone.insert("ut");
try stations.put("kone", &kone);
var ktwo = std.BufSet.init(arena.allocator());
try ktwo.insert("wa");
try ktwo.insert("id");
try ktwo.insert("mt");
try stations.put("ktwo", &ktwo);
var kthree = std.BufSet.init(arena.allocator());
try kthree.insert("or");
try kthree.insert("nv");
try kthree.insert("ca");
try stations.put("kthree", &kthree);
var kfour = std.BufSet.init(arena.allocator());
try kfour.insert("nv");
try kfour.insert("ut");
try stations.put("kfour", &kfour);
var kfive = std.BufSet.init(arena.allocator());
try kfive.insert("ca");
try kfive.insert("az");
try stations.put("kfive", &kfive);
var stations_covering = try setCovering(arena.allocator(), &stations, &states_needed);
for (stations_covering) |sc| {
std.debug.print("{s}\n", .{sc});
}
}
fn setCovering(allocator: mem.Allocator, stations: *std.StringHashMap(*std.BufSet), states_needed: *std.BufSet) ![][]const u8 {
var final_stations = std.BufSet.init(allocator);
while (states_needed.count() > 0) {
var best_station: []const u8 = undefined;
var states_covered: [][]const u8 = &[_][]const u8{};
var it = stations.iterator();
while (it.next()) |station| {
var covered = &std.ArrayList([]const u8).init(allocator);
try intersect(states_needed, station.value_ptr.*, covered);
if (covered.items.len > states_covered.len) {
best_station = station.key_ptr.*;
states_covered = covered.items;
} else covered.deinit();
}
difference(states_needed, states_covered);
try final_stations.insert(best_station);
}
var final_array = std.ArrayList([]const u8).init(allocator);
var i = final_stations.iterator();
while (i.next()) |key| {
try final_array.append(key.*);
}
return final_array.toOwnedSlice();
}
fn intersect(left: *std.BufSet, right: *std.BufSet, intersection: *std.ArrayList([]const u8)) !void {
var l_it = left.iterator();
var r_it = right.iterator();
while (l_it.next()) |l| {
while (r_it.next()) |r| {
if (std.mem.eql(u8, l.*, r.*)) {
try intersection.append(l.*);
}
}
}
}
fn difference(lessening: *std.BufSet, subtracting: [][]const u8) void {
var less_it = lessening.iterator();
while (less_it.next()) |less| {
for (subtracting) |sub| {
if (std.mem.eql(u8, less.*, sub)) {
lessening.remove(less.*);
}
}
}
}
test "setCovering" {
var gpa = heap.GeneralPurposeAllocator(.{}){};
var arena = heap.ArenaAllocator.init(gpa.allocator());
defer {
arena.deinit();
const leaked = gpa.deinit();
if (leaked) std.testing.expect(false) catch @panic("TEST FAIL"); //fail test; can't try in defer as defer is executed after we return
}
var states_needed_array = [_][]const u8{ "mt", "wa", "or", "id", "nv", "ut", "ca", "az" };
var states_needed = std.BufSet.init(arena.allocator());
for (states_needed_array) |sn| {
try states_needed.insert(sn);
}
var stations = std.StringHashMap(*std.BufSet).init(arena.allocator());
var kone = std.BufSet.init(arena.allocator());
try kone.insert("id");
try kone.insert("nv");
try kone.insert("ut");
try stations.put("kone", &kone);
var ktwo = std.BufSet.init(arena.allocator());
try ktwo.insert("wa");
try ktwo.insert("id");
try ktwo.insert("mt");
try stations.put("ktwo", &ktwo);
var kthree = std.BufSet.init(arena.allocator());
try kthree.insert("or");
try kthree.insert("nv");
try kthree.insert("ca");
try stations.put("kthree", &kthree);
var kfour = std.BufSet.init(arena.allocator());
try kfour.insert("nv");
try kfour.insert("ut");
try stations.put("kfour", &kfour);
var kfive = std.BufSet.init(arena.allocator());
try kfive.insert("ca");
try kfive.insert("az");
try stations.put("kfive", &kfive);
var stations_covering = try setCovering(arena.allocator(), &stations, &states_needed);
var expectedStations = &[_][]const u8{ "kone", "ktwo", "kfive", "kthree" };
for (stations_covering) |sc, i| {
try std.testing.expectEqualStrings(expectedStations[i], sc);
}
}