This commit is contained in:
Eric Mertens 2023-01-30 21:07:23 -08:00
parent 94f5cb3eb4
commit 542ccc7884

View File

@ -31,6 +31,9 @@
namespace { namespace {
namespace phx = boost::phoenix;
namespace qi = boost::spirit::qi;
/// @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
/// ///
@ -85,8 +88,6 @@ auto Parse(std::istream & in) -> std::vector<Room>
std::vector<Room> result; std::vector<Room> result;
std::string line; std::string line;
while (std::getline(in, line)) { while (std::getline(in, line)) {
namespace phx = boost::phoenix;
namespace qi = boost::spirit::qi;
using namespace qi::labels; using namespace qi::labels;
using It = std::string::const_iterator; using It = std::string::const_iterator;
@ -120,8 +121,10 @@ auto FlowsFirst(
std::vector<Room> & rooms std::vector<Room> & rooms
) -> std::size_t ) -> std::size_t
{ {
auto const zeros = boost::range::partition(rooms, [](auto const& room) { return room.flow > 0; }); using namespace phx::placeholders;
return zeros - rooms.begin();
auto const zeros = boost::range::partition(rooms, phx::bind(&Room::flow, arg1) > 0);
return std::distance(boost::begin(rooms), zeros);
} }
/// @brief Computes the distances between rooms and finds the address of the starting room /// @brief Computes the distances between rooms and finds the address of the starting room
@ -144,7 +147,8 @@ auto GenerateDistances(
// N is longer than any optimal distance by at least 1 // N is longer than any optimal distance by at least 1
std::fill_n(distances.data(), distances.num_elements(), N); std::fill_n(distances.data(), distances.num_elements(), N);
for (auto [i, room] : rooms | boost::adaptors::indexed()) { for (auto const i : boost::irange(rooms.size())) {
auto & room = rooms[i];
auto di = distances[i]; auto di = distances[i];
// each room is one away from adjacent rooms // each room is one away from adjacent rooms
@ -185,7 +189,6 @@ struct State {
/// @return Mapping of maximum flow achievable using a particular set of valves /// @return Mapping of maximum flow achievable using a particular set of valves
auto Routes( auto Routes(
std::size_t const start, std::size_t const start,
std::size_t const interesting,
std::uint64_t const initial_time, std::uint64_t const initial_time,
std::vector<Room> const& rooms, std::vector<Room> const& rooms,
distance_array<std::uint64_t> const& distances distance_array<std::uint64_t> const& distances
@ -206,7 +209,7 @@ auto Routes(
auto const distances_i = distances[state.location]; auto const distances_i = distances[state.location];
for (auto const j : boost::irange(interesting)) { for (auto const [j, room] : rooms | boost::adaptors::indexed()) {
// don't revisit a valve // don't revisit a valve
if (state.valves.test(j)) { continue; } if (state.valves.test(j)) { continue; }
@ -216,7 +219,7 @@ auto Routes(
if (cost >= state.time) { continue; } if (cost >= state.time) { continue; }
auto const time = state.time - cost; auto const time = state.time - cost;
auto const flow = state.flow + rooms[j].flow * time; auto const flow = state.flow + room.flow * time;
auto valves = state.valves; auto valves = state.valves;
valves.set(j); valves.set(j);
@ -234,33 +237,31 @@ auto Routes(
/// @return Maximum flow achievable /// @return Maximum flow achievable
auto Part1( auto Part1(
std::size_t const start, std::size_t const start,
std::size_t const interesting,
std::vector<Room> const& rooms, std::vector<Room> const& rooms,
distance_array<std::uint64_t> const& distances distance_array<std::uint64_t> const& distances
) -> std::uint64_t ) -> std::uint64_t
{ {
auto const routes = Routes(start, interesting, 30, rooms, distances); auto const routes = Routes(start, 30, rooms, distances);
return *boost::range::max_element(routes | boost::adaptors::map_values); return *boost::range::max_element(routes | boost::adaptors::map_values);
} }
/// @brief Maximize the water flow using two actos and 26 minutes /// @brief Maximize the water flow using two actors and 26 minutes
/// @param[in] start Index of the starting room /// @param[in] start Index of the starting room
/// @param[in] rooms Rooms from input file /// @param[in] rooms Rooms from input file
/// @param[in] distances Shortest distances between pairs of rooms /// @param[in] distances Shortest distances between pairs of rooms
/// @return Maximum flow achievable /// @return Maximum flow achievable
auto Part2( auto Part2(
std::size_t const start, std::size_t const start,
std::size_t const interesting,
std::vector<Room> const& rooms, std::vector<Room> const& rooms,
distance_array<std::uint64_t> const& distances distance_array<std::uint64_t> const& distances
) -> std::uint64_t ) -> std::uint64_t
{ {
auto const routes = Routes(start, interesting, 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; std::advance(it1, 1)) { for (auto it1 = routes.begin(); it1 != end; ++it1) {
for (auto it2 = std::next(it1); it2 != end; std::advance(it2, 1)) { for (auto it2 = std::next(it1); it2 != end; ++it2) {
// only consider pairs that have disjoint sets of valves // only consider pairs that have disjoint sets of valves
if ((it1->first & it2->first).none()) { if ((it1->first & it2->first).none()) {
best = std::max(best, it1->second + it2->second); best = std::max(best, it1->second + it2->second);
@ -287,10 +288,11 @@ Valve II has flow rate=0; tunnels lead to valves AA, JJ
Valve JJ has flow rate=21; tunnel leads to valve II Valve JJ has flow rate=21; tunnel leads to valve II
)"}; )"};
auto rooms = Parse(in); auto rooms = Parse(in);
auto const interesting = FlowsFirst(rooms); auto const n = FlowsFirst(rooms);
auto const [start, distances] = GenerateDistances(rooms); auto const [start, distances] = GenerateDistances(rooms);
CHECK(1651 == Part1(start, interesting, rooms, distances)); rooms.resize(n);
CHECK(1707 == Part2(start, interesting, rooms, distances)); CHECK(1651 == Part1(start, rooms, distances));
CHECK(1707 == Part2(start, rooms, distances));
} }
TEST_CASE("shortest path") { TEST_CASE("shortest path") {
@ -339,6 +341,7 @@ auto main(int argc, char** argv) -> int
auto rooms = Parse(*aocpp::Startup(argc, argv)); auto rooms = Parse(*aocpp::Startup(argc, argv));
auto const n = FlowsFirst(rooms); auto const n = FlowsFirst(rooms);
auto const [start, distances] = GenerateDistances(rooms); auto const [start, distances] = GenerateDistances(rooms);
std::cout << "Part 1: " << Part1(start, n, rooms, distances) << std::endl; rooms.resize(n);
std::cout << "Part 2: " << Part2(start, n, rooms, distances) << std::endl; std::cout << "Part 1: " << Part1(start, rooms, distances) << std::endl;
std::cout << "Part 2: " << Part2(start, rooms, distances) << std::endl;
} }