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:
|
/// This solution follows the following process:
|
||||||
/// 1. Parse the input source into a list of rooms
|
/// 1. Parse the input source into a list of rooms
|
||||||
/// 2. Compute the shortest paths between each room
|
/// 2. Optimization: put rooms with valves first in the array
|
||||||
/// 3. Enumerate all the paths through the graph to find maximum water flow per valve-set.
|
/// 3. Compute the shortest paths between each room
|
||||||
/// 4. Use the flow/valve summaries to compute the two answers.
|
/// 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 <bitset>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
@ -37,13 +38,16 @@ namespace {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using distance_array = boost::multi_array<T, 2>;
|
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
|
/// @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>
|
template <typename T>
|
||||||
auto ShortestDistances(distance_array<T> & dist) -> void
|
auto ShortestDistances(distance_array<T> & dist) -> void
|
||||||
{
|
{
|
||||||
// Floyd–Warshall_algorithm
|
|
||||||
auto const range = boost::irange(dist.size());
|
auto const range = boost::irange(dist.size());
|
||||||
for (auto const k : range) {
|
for (auto const k : range) {
|
||||||
for (auto const i : range) {
|
for (auto const i : range) {
|
||||||
|
@ -68,7 +72,7 @@ struct Room {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @brief Parse the input file
|
/// @brief Parse the input file
|
||||||
/// @param in input stream
|
/// @param[in,out] in input stream
|
||||||
/// @return Vector of parsed rooms, one per input line
|
/// @return Vector of parsed rooms, one per input line
|
||||||
///
|
///
|
||||||
/// The parser will consume input until the end of the stream.
|
/// 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"};
|
throw std::runtime_error{"bad input line"};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
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
|
/// @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
|
/// @returns starting index and distances
|
||||||
auto GenerateDistances(
|
auto GenerateDistances(
|
||||||
std::vector<Room> const& rooms
|
std::vector<Room> const& rooms
|
||||||
|
@ -161,13 +179,14 @@ struct State {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @brief Compute all the flows achievable with a set of values
|
/// @brief Compute all the flows achievable with a set of values
|
||||||
/// @param start Index of starting room
|
/// @param[in] start Index of starting room
|
||||||
/// @param initial_time Initial amount of time
|
/// @param[in] initial_time Initial amount of time
|
||||||
/// @param rooms Array of rooms from input file
|
/// @param[in] rooms Array of rooms from input file
|
||||||
/// @param distances Shortest paths between all pairs of rooms
|
/// @param[in] distances Shortest paths between all pairs of rooms
|
||||||
/// @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
|
||||||
|
@ -176,14 +195,6 @@ auto Routes(
|
||||||
// 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;
|
||||||
|
|
||||||
// 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
|
// Remaining states for depth first search
|
||||||
std::vector<State> states { State{initial_time, 0, start, {}} };
|
std::vector<State> states { State{initial_time, 0, start, {}} };
|
||||||
while (!states.empty()) {
|
while (!states.empty()) {
|
||||||
|
@ -196,7 +207,7 @@ auto Routes(
|
||||||
|
|
||||||
auto const distances_i = distances[state.location];
|
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
|
// don't revisit a valve
|
||||||
if (state.valves.test(j)) { continue; }
|
if (state.valves.test(j)) { continue; }
|
||||||
|
|
||||||
|
@ -218,32 +229,34 @@ auto Routes(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Maximize the water flow using a single actor and 30 minutes
|
/// @brief Maximize the water flow using a single actor and 30 minutes
|
||||||
/// @param start Index of the starting room
|
/// @param[in] start Index of the starting room
|
||||||
/// @param rooms Rooms from input file
|
/// @param[in] rooms Rooms from input file
|
||||||
/// @param 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 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, 30, rooms, distances);
|
auto const routes = Routes(start, interesting, 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 actos and 26 minutes
|
||||||
/// @param start Index of the starting room
|
/// @param[in] start Index of the starting room
|
||||||
/// @param rooms Rooms from input file
|
/// @param[in] rooms Rooms from input file
|
||||||
/// @param 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, 26, rooms, distances);
|
auto const routes = Routes(start, interesting, 26, rooms, distances);
|
||||||
auto const end = routes.end();
|
auto const end = routes.end();
|
||||||
|
|
||||||
std::uint64_t best {0};
|
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 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 const rooms = Parse(in);
|
auto rooms = Parse(in);
|
||||||
|
auto const interesting = FlowsFirst(rooms);
|
||||||
auto const [start, distances] = GenerateDistances(rooms);
|
auto const [start, distances] = GenerateDistances(rooms);
|
||||||
CHECK(1651 == Part1(start, rooms, distances));
|
CHECK(1651 == Part1(start, interesting, rooms, distances));
|
||||||
CHECK(1707 == Part2(start, rooms, distances));
|
CHECK(1707 == Part2(start, interesting, rooms, distances));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("shortest path") {
|
TEST_CASE("shortest path") {
|
||||||
|
@ -323,8 +337,9 @@ Valve JJ has flow rate=21; tunnel leads to valve II
|
||||||
/// @return 0 on success
|
/// @return 0 on success
|
||||||
auto main(int argc, char** argv) -> int
|
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);
|
auto const [start, distances] = GenerateDistances(rooms);
|
||||||
std::cout << "Part 1: " << Part1(start, rooms, distances) << std::endl;
|
std::cout << "Part 1: " << Part1(start, n, rooms, distances) << std::endl;
|
||||||
std::cout << "Part 2: " << Part2(start, rooms, distances) << std::endl;
|
std::cout << "Part 2: " << Part2(start, n, rooms, distances) << std::endl;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user