Add solution for "Day 23: LAN Party", part 2

This commit is contained in:
2025-06-23 21:24:16 +02:00
parent f6b9e7b034
commit 958adde4a2
4 changed files with 112 additions and 7 deletions

View File

@@ -15,6 +15,9 @@
#include <aoc/LanParty.hpp>
#include <algorithm>
#include <numeric>
const std::string LanParty::getPuzzleName() const
{
return "LAN Party";
@@ -32,12 +35,29 @@ void LanParty::processDataLine(const std::string& line)
void LanParty::finish()
{
size_t targetCliqueNumber{ 0 };
auto itTx = labelMap_.lower_bound(getFirstTxComputerName());
const auto itTxEnd = labelMap_.upper_bound(getLastTxComputerName());
while (itTx != itTxEnd)
{
computeInterconnectedThreeSetCount(itTx->second);
itTx++;
targetCliqueNumber = std::max(targetCliqueNumber, lan_.getDegree(itTx->second));
++itTx;
}
// Assumes that the graph is connected, but not complete. Otherwise we would start with targetCliqueNumber + 1.
// Also assumes that the clique number of the graph is close to its maximum degree, hence starting with a large
// target number.
bool isFound{ false };
while (!isFound && targetCliqueNumber > 2)
{
auto itTx = labelMap_.lower_bound(getFirstTxComputerName());
while (!isFound && itTx != itTxEnd)
{
isFound = findCompleteSubgraph(itTx->second, targetCliqueNumber);
++itTx;
}
--targetCliqueNumber;
}
}
@@ -74,20 +94,20 @@ void LanParty::computeInterconnectedThreeSetCount(const int vertexTx)
if (canProcessVertex(itFirstNeighbor->vertex, vertexTx))
{
auto itSecondNeighbor = itFirstNeighbor;
itSecondNeighbor++;
++itSecondNeighbor;
while (itSecondNeighbor != lan_.end())
{
if (canProcessVertex(itSecondNeighbor->vertex, vertexTx))
{
if (lan_.areAdjacent(itFirstNeighbor->vertex, itSecondNeighbor->vertex))
{
part1++;
++part1;
}
}
itSecondNeighbor++;
++itSecondNeighbor;
}
}
itFirstNeighbor++;
++itFirstNeighbor;
}
}
@@ -96,3 +116,72 @@ bool LanParty::canProcessVertex(const int vertexToCheck, const int vertexTx) con
const std::string& label{ lan_.getVertexData(vertexToCheck) };
return (label[0] != 't' || label > lan_.getVertexData(vertexTx));
}
bool LanParty::findCompleteSubgraph(const int vertex, const size_t targetSize)
{
// Validates that the degree of the start vertex is actually high enough for the target clique size.
if (targetSize > lan_.getDegree(vertex) + 1)
{
return false;
}
// Neighbors of start vertex.
std::vector<int> neighbors{};
neighbors.reserve(lan_.getDegree(vertex));
auto it = lan_.begin(vertex);
while (it != lan_.end())
{
neighbors.push_back(it->vertex);
++it;
}
// Combination of neighbors, i.e. 'neighbor[i]' is selected if and only if 'combination[i]' is true.
std::vector<bool> combination(targetSize - 1, true);
combination.resize(neighbors.size());
do
{
if (isSubgraphComplete(neighbors, combination))
{
part2 = calcPassword(neighbors, combination, vertex);
return true;
}
} while (std::prev_permutation(combination.begin(), combination.end()));
return false;
}
bool LanParty::isSubgraphComplete(const std::vector<int>& neighbors, const std::vector<bool>& combination) const
{
for (size_t i = 0; i < combination.size(); ++i)
{
if (combination[i])
{
for (size_t j = i + 1; j < combination.size(); ++j)
{
if (combination[j] && !lan_.areAdjacent(neighbors[i], neighbors[j]))
{
return false;
}
}
}
}
return true;
}
std::string LanParty::calcPassword(const std::vector<int>& neighbors, const std::vector<bool>& combination,
const int vertex) const
{
std::vector<std::string> completeSubgraph{ lan_.getVertexData(vertex) };
for (size_t i = 0; i < combination.size(); ++i)
{
if (combination[i])
{
completeSubgraph.push_back(lan_.getVertexData(neighbors[i]));
}
}
std::sort(completeSubgraph.begin(), completeSubgraph.end());
return std::accumulate(++completeSubgraph.begin(), completeSubgraph.end(), *completeSubgraph.begin(),
[](const std::string& acc, const std::string& x) { return acc + "," + x; });
}