Initial solution for 2022 day 16.
This commit is contained in:
153
2022/puzzle-16-01.cc
Normal file
153
2022/puzzle-16-01.cc
Normal file
@@ -0,0 +1,153 @@
|
||||
//
|
||||
// Created by Matthew Gretton-Dann on 16/12/2022.
|
||||
//
|
||||
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <numeric>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using Int = std::int64_t;
|
||||
using UInt = std::uint64_t;
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
struct ValveID
|
||||
{
|
||||
explicit constexpr ValveID(std::string const& id) noexcept : id_((id[0] - 'A') * 26 + id[1] - 'A')
|
||||
{
|
||||
}
|
||||
constexpr ValveID() noexcept = default;
|
||||
constexpr ValveID(ValveID const&) noexcept = default;
|
||||
constexpr auto operator=(ValveID const&) noexcept -> ValveID& = default;
|
||||
constexpr ValveID(ValveID&&) noexcept = default;
|
||||
constexpr auto operator=(ValveID&&) noexcept -> ValveID& = default;
|
||||
constexpr ~ValveID() noexcept = default;
|
||||
constexpr operator std::size_t() const noexcept { return id_; }
|
||||
|
||||
constexpr auto operator<=>(ValveID const& r) const noexcept -> std::strong_ordering
|
||||
{
|
||||
return id_ <=> r.id_;
|
||||
}
|
||||
|
||||
static constexpr auto max() -> ValveID
|
||||
{
|
||||
ValveID id;
|
||||
id.id_ = 26 * 26;
|
||||
return id;
|
||||
}
|
||||
|
||||
private:
|
||||
friend auto operator<<(std::ostream& os, ValveID id) -> std::ostream&;
|
||||
std::uint32_t id_{std::numeric_limits<std::uint32_t>::max()};
|
||||
};
|
||||
|
||||
auto operator<<(std::ostream& os, ValveID id) -> std::ostream&
|
||||
{
|
||||
return os << static_cast<char>((id.id_ / 26) + 'A') << static_cast<char>((id.id_ % 26) + 'A');
|
||||
}
|
||||
|
||||
struct State
|
||||
{
|
||||
ValveID current_{"AA"s};
|
||||
UInt total_rate_{0};
|
||||
std::vector<UInt> open_{std::vector<UInt>(ValveID::max(), false)};
|
||||
|
||||
auto operator<=>(State const& rhs) const noexcept -> std::strong_ordering
|
||||
{
|
||||
if (current_ != rhs.current_) {
|
||||
return current_ <=> rhs.current_;
|
||||
}
|
||||
for (UInt i{0}; i < open_.size(); ++i) {
|
||||
if (open_[i] != rhs.open_[i]) {
|
||||
return open_[i] <=> rhs.open_[i];
|
||||
}
|
||||
}
|
||||
return std::strong_ordering::equal;
|
||||
}
|
||||
};
|
||||
|
||||
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, ]+)"};
|
||||
std::vector<std::list<ValveID>> edges(ValveID::max(), std::list<ValveID>{});
|
||||
std::vector<UInt> rates(ValveID::max(), 0);
|
||||
UInt size{0};
|
||||
|
||||
while (std::getline(std::cin, line)) {
|
||||
std::smatch m;
|
||||
if (!std::regex_search(line, m, re)) {
|
||||
std::cerr << "Cannot interpret: " << line << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
ValveID const from{m.str(1)};
|
||||
rates.at(from) = std::stoull(m.str(2));
|
||||
std::string edge{};
|
||||
for (auto c : m.str(3)) {
|
||||
if (c == ',' || c == ' ') {
|
||||
continue;
|
||||
}
|
||||
edge += c;
|
||||
if (edge.size() == 2) {
|
||||
auto& l{edges.at(from)};
|
||||
auto id{ValveID{edge}};
|
||||
l.push_back(id);
|
||||
++size;
|
||||
edge.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::list<State> next_states;
|
||||
std::set<State> visited_states;
|
||||
visited_states.insert(State{});
|
||||
next_states.push_back(State{});
|
||||
UInt best_rate{0};
|
||||
|
||||
for (UInt time_left{30}; time_left > 0; --time_left) {
|
||||
std::list<State> const current_states{std::move(next_states)};
|
||||
std::cout << "Time: " << time_left << ": " << current_states.size() << " states to visit.\n";
|
||||
|
||||
if (current_states.empty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
next_states.clear();
|
||||
for (auto const& state : current_states) {
|
||||
if (!state.open_.at(state.current_) && rates.at(state.current_) != 0) {
|
||||
State next_state{state};
|
||||
next_state.open_.at(state.current_) = true;
|
||||
next_state.total_rate_ += rates.at(state.current_) * (time_left - 1);
|
||||
best_rate = std::max(best_rate, next_state.total_rate_);
|
||||
next_states.push_back(next_state);
|
||||
visited_states.erase(next_state);
|
||||
visited_states.insert(next_state);
|
||||
}
|
||||
|
||||
for (auto e : edges[state.current_]) {
|
||||
State next_state{state};
|
||||
next_state.current_ = e;
|
||||
auto vit = visited_states.find(next_state);
|
||||
if (vit != visited_states.end() && vit->total_rate_ >= next_state.total_rate_) {
|
||||
continue;
|
||||
}
|
||||
if (vit != visited_states.end()) {
|
||||
visited_states.erase(vit);
|
||||
}
|
||||
next_states.push_back(next_state);
|
||||
visited_states.insert(next_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Maximum flow rate: " << best_rate << "\n";
|
||||
return EXIT_SUCCESS;
|
||||
}
|
203
2022/puzzle-16-02.cc
Normal file
203
2022/puzzle-16-02.cc
Normal file
@@ -0,0 +1,203 @@
|
||||
//
|
||||
// Created by Matthew Gretton-Dann on 16/12/2022.
|
||||
//
|
||||
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <numeric>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using Int = std::int64_t;
|
||||
using UInt = std::uint64_t;
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
struct ValveID
|
||||
{
|
||||
explicit constexpr ValveID(std::string const& id) noexcept : id_((id[0] - 'A') * 26 + id[1] - 'A')
|
||||
{
|
||||
}
|
||||
explicit constexpr ValveID(UInt i) noexcept : id_(i) {}
|
||||
constexpr ValveID() noexcept = default;
|
||||
constexpr ValveID(ValveID const&) noexcept = default;
|
||||
constexpr auto operator=(ValveID const&) noexcept -> ValveID& = default;
|
||||
constexpr ValveID(ValveID&&) noexcept = default;
|
||||
constexpr auto operator=(ValveID&&) noexcept -> ValveID& = default;
|
||||
constexpr ~ValveID() noexcept = default;
|
||||
constexpr operator std::size_t() const noexcept { return id_; }
|
||||
|
||||
constexpr auto operator<=>(ValveID const& r) const noexcept -> std::strong_ordering
|
||||
{
|
||||
return id_ <=> r.id_;
|
||||
}
|
||||
|
||||
static constexpr auto max() -> ValveID
|
||||
{
|
||||
ValveID id;
|
||||
id.id_ = 26 * 26;
|
||||
return id;
|
||||
}
|
||||
|
||||
private:
|
||||
friend auto operator<<(std::ostream& os, ValveID id) -> std::ostream&;
|
||||
std::uint32_t id_{std::numeric_limits<std::uint32_t>::max()};
|
||||
};
|
||||
|
||||
auto operator<<(std::ostream& os, ValveID id) -> std::ostream&
|
||||
{
|
||||
return os << static_cast<char>((id.id_ / 26) + 'A') << static_cast<char>((id.id_ % 26) + 'A');
|
||||
}
|
||||
|
||||
struct State
|
||||
{
|
||||
ValveID id_[2]{ValveID("AA"s), ValveID("AA"s)};
|
||||
UInt next_time_[2]{26, 26};
|
||||
UInt total_rate_{0};
|
||||
std::vector<char> open_{std::vector<char>(ValveID::max(), false)};
|
||||
|
||||
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 (UInt i{0}; i < open_.size(); ++i) {
|
||||
if (open_[i] != rhs.open_[i]) {
|
||||
return open_[i] <=> rhs.open_[i];
|
||||
}
|
||||
}
|
||||
return std::strong_ordering::equal;
|
||||
}
|
||||
};
|
||||
|
||||
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, ]+)"};
|
||||
std::vector<std::vector<UInt>> edges(ValveID::max(), std::vector<UInt>(ValveID::max(), 0));
|
||||
std::vector<UInt> rates(ValveID::max(), 0);
|
||||
UInt size{0};
|
||||
|
||||
while (std::getline(std::cin, line)) {
|
||||
std::smatch m;
|
||||
if (!std::regex_search(line, m, re)) {
|
||||
std::cerr << "Cannot interpret: " << line << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
ValveID const from{m.str(1)};
|
||||
rates.at(from) = std::stoull(m.str(2));
|
||||
std::string edge{};
|
||||
for (auto c : m.str(3)) {
|
||||
if (c == ',' || c == ' ') {
|
||||
continue;
|
||||
}
|
||||
edge += c;
|
||||
if (edge.size() == 2) {
|
||||
edges.at(from).at(ValveID{edge}) = 1;
|
||||
edge.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool changed{true};
|
||||
while (changed) {
|
||||
changed = false;
|
||||
for (UInt e1{0}; e1 < ValveID::max(); ++e1) {
|
||||
for (UInt e2{0}; e2 < ValveID::max(); ++e2) {
|
||||
if (edges[e1][e2] != 0) {
|
||||
for (UInt e3{0}; e3 < ValveID::max(); ++e3) {
|
||||
if (edges[e2][e3] == 1 && edges[e1][e3] == 0) {
|
||||
edges[e1][e3] = edges[e1][e2] + 1;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Turning default state on.
|
||||
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.
|
||||
for (UInt e1{0}; e1 < ValveID::max(); ++e1) {
|
||||
for (UInt e2{0}; e2 < ValveID::max(); ++e2) {
|
||||
if (rates[e2] == 0) {
|
||||
edges[e1][e2] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::list<State> current_states;
|
||||
std::set<State> visited_states;
|
||||
visited_states.insert(State{});
|
||||
current_states.push_back(State{});
|
||||
UInt best_rate{0};
|
||||
|
||||
for (UInt time_left{26}; time_left > 0; --time_left) {
|
||||
for (std::size_t mover{0}; mover < 2; ++mover) {
|
||||
std::cout << "Time: " << time_left << ": " << current_states.size()
|
||||
<< " states to visit. Person " << mover << "\n";
|
||||
|
||||
if (current_states.empty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto it{current_states.begin()}; it != current_states.end();) {
|
||||
if (it->next_time_[mover] != time_left) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
ValveID from{it->id_[mover]};
|
||||
|
||||
for (UInt to{0}; to < ValveID::max(); ++to) {
|
||||
if (edges[from][to] == 0) {
|
||||
continue;
|
||||
}
|
||||
if (edges[from][to] + 1 >= time_left) {
|
||||
continue;
|
||||
}
|
||||
if (it->open_[to]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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.total_rate_ += (next_state.next_time_[mover]) * rates[to];
|
||||
best_rate = std::max(best_rate, next_state.total_rate_);
|
||||
|
||||
auto vit = visited_states.find(next_state);
|
||||
if (vit != visited_states.end() && vit->total_rate_ >= next_state.total_rate_) {
|
||||
continue;
|
||||
}
|
||||
if (vit != visited_states.end()) {
|
||||
visited_states.erase(vit);
|
||||
}
|
||||
visited_states.insert(next_state);
|
||||
current_states.push_back(next_state);
|
||||
}
|
||||
it = current_states.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Maximum flow rate: " << best_rate << "\n";
|
||||
return EXIT_SUCCESS;
|
||||
}
|
Reference in New Issue
Block a user