documentation and tiny speedup
This commit is contained in:
parent
30709c7bd7
commit
39c3d59370
83
2022/16.cpp
83
2022/16.cpp
|
@ -3,9 +3,10 @@
|
|||
///
|
||||
/// This solution follows the following process:
|
||||
/// 1. Parse the input source into a list of rooms
|
||||
/// 2. Compute the shortest paths between each room
|
||||
/// 3. Enumerate all the paths through the graph to find maximum water flow per valve-set.
|
||||
/// 4. Use the flow/valve summaries to compute the two answers.
|
||||
/// 2. Optimization: put rooms with valves first in the array
|
||||
/// 3. Compute the shortest paths between each room
|
||||
/// 4. Enumerate all the paths through the graph to find maximum water flow per valve-set.
|
||||
/// 5. Use the flow/valve summaries to compute the two answers.
|
||||
|
||||
#include <bitset>
|
||||
#include <cstdint>
|
||||
|
@ -37,13 +38,16 @@ namespace {
|
|||
template <typename T>
|
||||
using distance_array = boost::multi_array<T, 2>;
|
||||
|
||||
/// @brief Update distance matrix with transitive shortest paths
|
||||
/// @brief Update single-step distance matrix with transitive shortest paths
|
||||
/// @tparam T distance type
|
||||
/// @param dist Single-step distances between nodes (must be square)
|
||||
/// @param[in,out] dist distance matrix
|
||||
///
|
||||
/// This implementation uses the Floyd–Warshall algorithm and assumes that
|
||||
/// there are no negative-cost cycles. It also assumes that a path exists
|
||||
/// between all pairs of nodes.
|
||||
template <typename T>
|
||||
auto ShortestDistances(distance_array<T> & dist) -> void
|
||||
{
|
||||
// Floyd–Warshall_algorithm
|
||||
auto const range = boost::irange(dist.size());
|
||||
for (auto const k : range) {
|
||||
for (auto const i : range) {
|
||||
|
@ -68,7 +72,7 @@ struct Room {
|
|||
};
|
||||
|
||||
/// @brief Parse the input file
|
||||
/// @param in input stream
|
||||
/// @param[in,out] in input stream
|
||||
/// @return Vector of parsed rooms, one per input line
|
||||
///
|
||||
/// The parser will consume input until the end of the stream.
|
||||
|
@ -105,10 +109,24 @@ auto Parse(std::istream & in) -> std::vector<Room>
|
|||
throw std::runtime_error{"bad input line"};
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// @brief Rearrange the rooms so that those with flows come first
|
||||
/// @param[in,out] rooms vector of rooms that gets reordered
|
||||
/// @return number of rooms with with non-zero flows
|
||||
auto FlowsFirst(
|
||||
std::vector<Room> & rooms
|
||||
) -> std::size_t
|
||||
{
|
||||
// Put all of the rooms with vavles at the begining.
|
||||
auto const zeros = boost::range::partition(rooms, [](auto const& room) { return room.flow > 0; });
|
||||
return zeros - rooms.begin();
|
||||
}
|
||||
|
||||
/// @brief Computes the distances between rooms and finds the address of the starting room
|
||||
/// @param[in] rooms input list of rooms
|
||||
/// @returns starting index and distances
|
||||
auto GenerateDistances(
|
||||
std::vector<Room> const& rooms
|
||||
|
@ -161,13 +179,14 @@ struct State {
|
|||
};
|
||||
|
||||
/// @brief Compute all the flows achievable with a set of values
|
||||
/// @param start Index of starting room
|
||||
/// @param initial_time Initial amount of time
|
||||
/// @param rooms Array of rooms from input file
|
||||
/// @param distances Shortest paths between all pairs of rooms
|
||||
/// @param[in] start Index of starting room
|
||||
/// @param[in] initial_time Initial amount of time
|
||||
/// @param[in] rooms Array of rooms from input file
|
||||
/// @param[in] distances Shortest paths between all pairs of rooms
|
||||
/// @return Mapping of maximum flow achievable using a particular set of valves
|
||||
auto Routes(
|
||||
std::size_t const start,
|
||||
std::size_t const interesting,
|
||||
std::uint64_t const initial_time,
|
||||
std::vector<Room> const& rooms,
|
||||
distance_array<std::uint64_t> const& distances
|
||||
|
@ -176,14 +195,6 @@ auto Routes(
|
|||
// Maximal flow seen at each set of open valves
|
||||
std::unordered_map<Valves, std::uint64_t> result;
|
||||
|
||||
// Figure out which rooms have flow and are worth visiting at all
|
||||
std::vector<std::size_t> interesting_rooms;
|
||||
for (auto [i, room] : rooms | boost::adaptors::indexed()) {
|
||||
if (room.flow > 0) {
|
||||
interesting_rooms.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Remaining states for depth first search
|
||||
std::vector<State> states { State{initial_time, 0, start, {}} };
|
||||
while (!states.empty()) {
|
||||
|
@ -196,7 +207,7 @@ auto Routes(
|
|||
|
||||
auto const distances_i = distances[state.location];
|
||||
|
||||
for (auto const j : interesting_rooms) {
|
||||
for (auto const j : boost::irange(interesting)) {
|
||||
// don't revisit a valve
|
||||
if (state.valves.test(j)) { continue; }
|
||||
|
||||
|
@ -218,32 +229,34 @@ auto Routes(
|
|||
}
|
||||
|
||||
/// @brief Maximize the water flow using a single actor and 30 minutes
|
||||
/// @param start Index of the starting room
|
||||
/// @param rooms Rooms from input file
|
||||
/// @param distances Shortest distances between pairs of rooms
|
||||
/// @param[in] start Index of the starting room
|
||||
/// @param[in] rooms Rooms from input file
|
||||
/// @param[in] distances Shortest distances between pairs of rooms
|
||||
/// @return Maximum flow achievable
|
||||
auto Part1(
|
||||
std::size_t const start,
|
||||
std::size_t const interesting,
|
||||
std::vector<Room> const& rooms,
|
||||
distance_array<std::uint64_t> const& distances
|
||||
) -> std::uint64_t
|
||||
{
|
||||
auto const routes = Routes(start, 30, rooms, distances);
|
||||
auto const routes = Routes(start, interesting, 30, rooms, distances);
|
||||
return *boost::range::max_element(routes | boost::adaptors::map_values);
|
||||
}
|
||||
|
||||
/// @brief Maximize the water flow using two actos and 26 minutes
|
||||
/// @param start Index of the starting room
|
||||
/// @param rooms Rooms from input file
|
||||
/// @param distances Shortest distances between pairs of rooms
|
||||
/// @param[in] start Index of the starting room
|
||||
/// @param[in] rooms Rooms from input file
|
||||
/// @param[in] distances Shortest distances between pairs of rooms
|
||||
/// @return Maximum flow achievable
|
||||
auto Part2(
|
||||
std::size_t const start,
|
||||
std::size_t const interesting,
|
||||
std::vector<Room> const& rooms,
|
||||
distance_array<std::uint64_t> const& distances
|
||||
) -> std::uint64_t
|
||||
{
|
||||
auto const routes = Routes(start, 26, rooms, distances);
|
||||
auto const routes = Routes(start, interesting, 26, rooms, distances);
|
||||
auto const end = routes.end();
|
||||
|
||||
std::uint64_t best {0};
|
||||
|
@ -274,10 +287,11 @@ Valve HH has flow rate=22; tunnel leads to valve GG
|
|||
Valve II has flow rate=0; tunnels lead to valves AA, JJ
|
||||
Valve JJ has flow rate=21; tunnel leads to valve II
|
||||
)"};
|
||||
auto const rooms = Parse(in);
|
||||
auto rooms = Parse(in);
|
||||
auto const interesting = FlowsFirst(rooms);
|
||||
auto const [start, distances] = GenerateDistances(rooms);
|
||||
CHECK(1651 == Part1(start, rooms, distances));
|
||||
CHECK(1707 == Part2(start, rooms, distances));
|
||||
CHECK(1651 == Part1(start, interesting, rooms, distances));
|
||||
CHECK(1707 == Part2(start, interesting, rooms, distances));
|
||||
}
|
||||
|
||||
TEST_CASE("shortest path") {
|
||||
|
@ -323,8 +337,9 @@ Valve JJ has flow rate=21; tunnel leads to valve II
|
|||
/// @return 0 on success
|
||||
auto main(int argc, char** argv) -> int
|
||||
{
|
||||
auto const rooms = Parse(*aocpp::Startup(argc, argv));
|
||||
auto rooms = Parse(*aocpp::Startup(argc, argv));
|
||||
auto const n = FlowsFirst(rooms);
|
||||
auto const [start, distances] = GenerateDistances(rooms);
|
||||
std::cout << "Part 1: " << Part1(start, rooms, distances) << std::endl;
|
||||
std::cout << "Part 2: " << Part2(start, rooms, distances) << std::endl;
|
||||
std::cout << "Part 1: " << Part1(start, n, rooms, distances) << std::endl;
|
||||
std::cout << "Part 2: " << Part2(start, n, rooms, distances) << std::endl;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user