aoc2024: Implement day23 solution

I'm not proud of my implementation to last 2 days, but they work in a
reasonable time. I might optimize them later...

Problem is largely with my hash structures. Preallocating a lot of
elements before running day22 makes it run in less than half of the
current time.
This commit is contained in:
Bananymous 2024-12-23 21:20:47 +02:00
parent af8156f310
commit d035068982
3 changed files with 181 additions and 0 deletions

View File

@ -21,6 +21,7 @@ set(AOC2024_PROJECTS
day20
day21
day22
day23
full
)

View File

@ -0,0 +1,9 @@
set(SOURCES
main.cpp
)
add_executable(aoc2024_day23 ${SOURCES})
banan_link_library(aoc2024_day23 ban)
banan_link_library(aoc2024_day23 libc)
install(TARGETS aoc2024_day23 OPTIONAL)

View File

@ -0,0 +1,171 @@
#include <BAN/HashMap.h>
#include <BAN/HashSet.h>
#include <BAN/Sort.h>
#include <BAN/String.h>
#include <inttypes.h>
#include <stdio.h>
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<u32, BAN::Vector<u32>> connections;
BAN::HashSet<u32> all_nodes;
};
static ParseInputResult parse_input(FILE* fp)
{
BAN::HashMap<u32, BAN::Vector<u32>> connections;
BAN::HashSet<u32> 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<u32, BAN::Vector<u32>>& connections, const BAN::Vector<u32>& 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<u32> maximum_clique(const BAN::HashMap<u32, BAN::Vector<u32>>& connections, const BAN::Vector<u32>& nodes, BAN::Vector<u32>&& current, usize idx)
{
if (idx == nodes.size())
return current;
BAN::Vector<u32> result;
const u32 check = nodes[nodes.size() - idx - 1];
if (is_connected_to_all_nodes(connections, current, check))
{
BAN::Vector<u32> 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<u32> 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);
}