#include #include #include #include #include #include using i8 = int8_t; using i16 = int16_t; using i32 = int32_t; using i64 = int64_t; using isize = ssize_t; using u8 = uint8_t; using u16 = uint16_t; using u32 = uint32_t; using u64 = uint64_t; using usize = size_t; struct ParseInputResult { BAN::HashMap> connections; BAN::HashSet all_nodes; }; static ParseInputResult parse_input(FILE* fp) { BAN::HashMap> connections; BAN::HashSet all_nodes; char buffer[16]; while (fgets(buffer, sizeof(buffer), fp)) { ASSERT(buffer[2] == '-' && buffer[5] == '\n'); const u16 lhs = (buffer[0] << 8) | buffer[1]; const u16 rhs = (buffer[3] << 8) | buffer[4]; const u16 min = BAN::Math::min(lhs, rhs); const u16 max = BAN::Math::max(lhs, rhs); auto it = connections.find(min); if (it == connections.end()) it = MUST(connections.emplace(min)); MUST(it->value.push_back(max)); MUST(all_nodes.insert(lhs)); MUST(all_nodes.insert(rhs)); } return { .connections = BAN::move(connections), .all_nodes = BAN::move(all_nodes), }; } i64 part1(FILE* fp) { auto [connections, _] = parse_input(fp); u64 result = 0; for (const auto& [con1, dst1] : connections) { for (const u16 con2 : dst1) { auto it = connections.find(con2); if (it == connections.end()) continue; for (const u16 con3 : it->value) { if (!dst1.contains(con3)) continue; result += ((char)(con1 >> 8) == 't') || ((char)(con2 >> 8) == 't') || ((char)(con3 >> 8) == 't'); } } } return result; } static bool is_connected_to_all_nodes(const BAN::HashMap>& connections, const BAN::Vector& nodes, u32 test) { if (nodes.empty()) return true; auto it = connections.find(test); if (it == connections.end()) return false; const auto& test_con = it->value; usize found = 0; for (usize i = 0; i < test_con.size() && found < nodes.size(); i++) if (test_con[i] == nodes[found]) found++; return found == nodes.size(); } static BAN::Vector maximum_clique(const BAN::HashMap>& connections, const BAN::Vector& nodes, BAN::Vector&& current, usize idx) { if (idx == nodes.size()) return current; BAN::Vector result; const u32 check = nodes[nodes.size() - idx - 1]; if (is_connected_to_all_nodes(connections, current, check)) { BAN::Vector temp; MUST(temp.resize(current.size() + 1)); for (usize i = 0; i < current.size(); i++) temp[i + 1] = current[i]; temp[0] = check; result = maximum_clique(connections, nodes, BAN::move(temp), idx + 1); } auto temp = maximum_clique(connections, nodes, BAN::move(current), idx + 1); if (temp.size() > result.size()) result = BAN::move(temp); return result; } BAN::String part2(FILE* fp) { auto [connections, set_all_nodes] = parse_input(fp); for (auto& [_, conn] : connections) BAN::sort::sort(conn.begin(), conn.end()); BAN::Vector all_nodes; MUST(all_nodes.reserve(set_all_nodes.size())); for (const u32 node : set_all_nodes) MUST(all_nodes.push_back(node)); BAN::sort::sort(all_nodes.begin(), all_nodes.end()); auto clique = maximum_clique(connections, all_nodes, {}, 0); BAN::String result; for (u32 node : clique) MUST(result.append(MUST(BAN::String::formatted("{}{},", (char)(node >> 8), (char)node)))); if (!result.empty()) result.pop_back(); return result; } int main(int argc, char** argv) { const char* file_path = "/usr/share/aoc2024/day23_input.txt"; if (argc >= 2) file_path = argv[1]; FILE* fp = fopen(file_path, "r"); if (fp == nullptr) { perror("fopen"); return 1; } printf("part1: %" PRId64 "\n", part1(fp)); fseek(fp, 0, SEEK_SET); printf("part2: %s\n", part2(fp).data()); fclose(fp); }