This commit is contained in:
Eric Mertens 2023-02-01 11:23:04 -08:00
parent 586b3244a4
commit 57004e1a79
2 changed files with 27 additions and 21 deletions

View File

@ -34,6 +34,12 @@ namespace {
namespace phx = boost::phoenix; namespace phx = boost::phoenix;
namespace qi = boost::spirit::qi; namespace qi = boost::spirit::qi;
/// @brief The starting room as defined in the problem statement
constexpr char const* STARTING_ROOM = "AA";
/// @brief Arbitrary number of valves this solution supports
constexpr std::size_t MAX_VALVES = 64;
/// @brief Array of distances from one node to another. /// @brief Array of distances from one node to another.
/// @tparam T distance type /// @tparam T distance type
/// ///
@ -55,15 +61,14 @@ auto ShortestDistances(distance_array<T> & dist) -> void
for (auto const k : range) { for (auto const k : range) {
for (auto const i : range) { for (auto const i : range) {
for (auto const j : range) { for (auto const j : range) {
auto const new_dist = dist[i][k] + dist[k][j]; auto const d_ikj = dist[i][k] + dist[k][j];
auto & old_dist = dist[i][j]; auto & d_ij = dist[i][j];
if (old_dist > new_dist) old_dist = new_dist; if (d_ij > d_ikj) d_ij = d_ijk;
} }
} }
} }
} }
/// @struct Room
/// @brief A single record from the problem input. /// @brief A single record from the problem input.
struct Room { struct Room {
/// @brief Name of the room /// @brief Name of the room
@ -122,7 +127,6 @@ auto FlowsFirst(
) -> std::size_t ) -> std::size_t
{ {
using namespace phx::placeholders; using namespace phx::placeholders;
auto const zeros = boost::range::partition(rooms, phx::bind(&Room::flow, arg1) > 0); auto const zeros = boost::range::partition(rooms, phx::bind(&Room::flow, arg1) > 0);
return std::distance(boost::begin(rooms), zeros); return std::distance(boost::begin(rooms), zeros);
} }
@ -134,7 +138,7 @@ auto GenerateDistances(
std::vector<Room> const& rooms std::vector<Room> const& rooms
) -> std::pair<std::size_t, distance_array<std::uint64_t>> ) -> std::pair<std::size_t, distance_array<std::uint64_t>>
{ {
auto const N = rooms.size(); auto const N {rooms.size()};
// Associate the names and indexes of each room // Associate the names and indexes of each room
std::unordered_map<std::string, std::size_t> names; std::unordered_map<std::string, std::size_t> names;
@ -144,15 +148,14 @@ auto GenerateDistances(
distance_array<std::uint64_t> distances{boost::extents[N][N]}; distance_array<std::uint64_t> distances{boost::extents[N][N]};
// N is longer than any optimal distance by at least 1
std::fill_n(distances.data(), distances.num_elements(), N);
for (auto const i : boost::irange(rooms.size())) { for (auto const i : boost::irange(rooms.size())) {
auto & room = rooms[i];
auto di = distances[i]; auto di = distances[i];
// Default value: N is longer than any optimal distance by at least 1
boost::range::fill(di, N);
// each room is one away from adjacent rooms // each room is one away from adjacent rooms
for (auto const& name : room.connections) { for (auto const& name : rooms[i].connections) {
di[names[name]] = 1; di[names[name]] = 1;
} }
@ -162,13 +165,12 @@ auto GenerateDistances(
ShortestDistances(distances); ShortestDistances(distances);
return {names.at("AA"), std::move(distances)}; return {names.at(STARTING_ROOM), std::move(distances)};
} }
/// @brief Bitset used to track which valves have been turned on /// @brief Bitset used to track which valves have been turned on
using Valves = std::bitset<64>; using Valves = std::bitset<MAX_VALVES>;
/// @struct State
/// @brief Intermediate states for depth-first search in Routes /// @brief Intermediate states for depth-first search in Routes
struct State { struct State {
/// @brief Time remaining /// @brief Time remaining
@ -194,11 +196,15 @@ auto Routes(
distance_array<std::uint64_t> const& distances distance_array<std::uint64_t> const& distances
) -> std::unordered_map<Valves, std::uint64_t> ) -> std::unordered_map<Valves, std::uint64_t>
{ {
if (rooms.size() > MAX_VALVES) {
throw std::runtime_error{"Too many valves"};
}
// Maximal flow seen at each set of open valves // Maximal flow seen at each set of open valves
std::unordered_map<Valves, std::uint64_t> result; std::unordered_map<Valves, std::uint64_t> result;
// Remaining states for depth first search // Remaining states for depth first search
std::vector<State> states { State{initial_time, 0, start, {}} }; std::vector<State> states {{initial_time, 0, start, {}}};
while (!states.empty()) { while (!states.empty()) {
auto const state = states.back(); auto const state = states.back();
states.pop_back(); states.pop_back();
@ -258,7 +264,7 @@ auto Part2(
{ {
auto const routes = Routes(start, 26, rooms, distances); auto const routes = Routes(start, 26, rooms, distances);
auto const end = routes.end(); auto const end = routes.end();
std::uint64_t best {0}; std::uint64_t best {0};
for (auto it1 = routes.begin(); it1 != end; ++it1) { for (auto it1 = routes.begin(); it1 != end; ++it1) {
for (auto it2 = std::next(it1); it2 != end; ++it2) { for (auto it2 = std::next(it1); it2 != end; ++it2) {
@ -273,7 +279,7 @@ auto Part2(
} // namespace } // namespace
/// @brief Print solutions to parts 1 and 2 /// @brief Print solutions to parts 1 and 2
/// @param[in,out] in input text /// @param[in,out] in input text
/// @param[in,out] out output text /// @param[in,out] out output text
auto Main(std::istream & in, std::ostream & out) -> void auto Main(std::istream & in, std::ostream & out) -> void
@ -340,4 +346,4 @@ Valve JJ has flow rate=21; tunnel leads to valve II
CHECK(distances[3][2] == 1); CHECK(distances[3][2] == 1);
CHECK(distances[3][3] == 0); CHECK(distances[3][3] == 0);
} }
} }

View File

@ -53,12 +53,12 @@ namespace
case 't': case 't':
result.run_tests = true; result.run_tests = true;
break; break;
case ':':
std::cerr << "Missing argument to -" << char(optopt) << std::endl;
return {};
case 'h': case 'h':
result.print_help = true; result.print_help = true;
break; break;
case ':':
std::cerr << "Missing argument to -" << char(optopt) << std::endl;
return {};
case '?': case '?':
default: default:
std::cerr << "Unknown flag -" << char(optopt) << std::endl; std::cerr << "Unknown flag -" << char(optopt) << std::endl;