diff --git a/2022/puzzle-16-02.cc b/2022/puzzle-16-02.cc index 5e0d482..b200161 100644 --- a/2022/puzzle-16-02.cc +++ b/2022/puzzle-16-02.cc @@ -6,15 +6,13 @@ #include #include #include -#include #include #include #include #include #include -using Int = std::int64_t; -using UInt = std::uint64_t; +using UInt = std::uint32_t; using namespace std::string_literals; @@ -59,18 +57,26 @@ struct State ValveID id_[2]{ValveID("AA"s), ValveID("AA"s)}; UInt next_time_[2]{26, 26}; UInt total_rate_{0}; - std::vector open_{std::vector(ValveID::max(), false)}; + std::vector open_{std::vector(ValveID::max(), 0)}; auto operator<=>(State const& rhs) const noexcept -> std::strong_ordering { - for (unsigned i = 0; i < 2; ++i) { - if (id_[i] != rhs.id_[i]) { - return id_[i] <=> rhs.id_[i]; - } - if (next_time_[i] != rhs.next_time_[i]) { - return next_time_[i] <=> rhs.next_time_[i]; - } + /* For comparisons - we don't care whether the human or elephant is at each site - only that one + * of them is. So we compare the max and min + */ + auto lm1{std::max(id_[0], id_[1])}; + auto rm1{std::max(rhs.id_[0], rhs.id_[1])}; + auto lm2{std::min(id_[0], id_[1])}; + auto rm2{std::min(rhs.id_[0], rhs.id_[1])}; + if (lm1 != rm1) { + return lm1 <=> rm1; } + if (lm2 != rm2) { + return lm2 <=> rm2; + } + + /* We do not care about the time left or the score for sorting. */ + for (UInt i{0}; i < open_.size(); ++i) { if (open_[i] != rhs.open_[i]) { return open_[i] <=> rhs.open_[i]; @@ -85,9 +91,12 @@ auto main() -> int std::string line; std::regex const re{ "Valve ([A-Z][A-Z]) has flow rate=(\\d+); tunnels? leads? to valves? ([A-Z, ]+)"}; + + /* The edges vector will end up so that edges[a][b] will contain the number of seconds it will + * take to go from a to b, or 0 if there are no routes, or it is not worth stopping at b. + */ std::vector> edges(ValveID::max(), std::vector(ValveID::max(), 0)); std::vector rates(ValveID::max(), 0); - UInt size{0}; while (std::getline(std::cin, line)) { std::smatch m; @@ -110,6 +119,9 @@ auto main() -> int } } + /* edges currently contains all routes that are one step away. Now update it so that all possible + * moves are possible. + */ bool changed{true}; while (changed) { changed = false; @@ -127,12 +139,12 @@ auto main() -> int } } - // Turning default state on. + // Moves from `a` to `a` are worthless. for (UInt id{0}; id < ValveID::max(); ++id) { edges[id][id] = 0; } - // If rate at a site is zero then change the cost to zero. + // If rate at a site is zero then change the cost to zero, as there is no point stopping here. for (UInt e1{0}; e1 < ValveID::max(); ++e1) { for (UInt e2{0}; e2 < ValveID::max(); ++e2) { if (rates[e2] == 0) { @@ -156,15 +168,18 @@ auto main() -> int break; } + std::list next_states; for (auto it{current_states.begin()}; it != current_states.end();) { + // Is it the current time to modify this item? if (it->next_time_[mover] != time_left) { ++it; continue; } - ValveID from{it->id_[mover]}; + ValveID const from{it->id_[mover]}; for (UInt to{0}; to < ValveID::max(); ++to) { + // Determine were to move to. if (edges[from][to] == 0) { continue; } @@ -177,12 +192,14 @@ auto main() -> int State next_state{*it}; next_state.id_[mover] = ValveID(to); - next_state.open_[to] = true; - auto timestep = edges[from][to] + 1; - next_state.next_time_[mover] -= timestep; + next_state.open_[to] = 1; + auto const time_step{edges[from][to] + 1}; + next_state.next_time_[mover] -= time_step; next_state.total_rate_ += (next_state.next_time_[mover]) * rates[to]; best_rate = std::max(best_rate, next_state.total_rate_); + // Ensure that this is going to make things better: Either we've never visited the state + // before. Or if we have then this version has a higher score. auto vit = visited_states.find(next_state); if (vit != visited_states.end() && vit->total_rate_ >= next_state.total_rate_) { continue; @@ -191,10 +208,11 @@ auto main() -> int visited_states.erase(vit); } visited_states.insert(next_state); - current_states.push_back(next_state); + next_states.push_back(next_state); } it = current_states.erase(it); } + current_states.splice(current_states.end(), next_states); } }