167 lines
4.0 KiB
C++
167 lines
4.0 KiB
C++
|
#include <cstdint>
|
||
|
#include <iostream>
|
||
|
#include <sstream>
|
||
|
#include <stdexcept>
|
||
|
#include <string>
|
||
|
#include <tuple>
|
||
|
#include <vector>
|
||
|
#include <map>
|
||
|
#include <bitset>
|
||
|
|
||
|
#include <boost/spirit/home/qi.hpp>
|
||
|
#include <boost/range/irange.hpp>
|
||
|
|
||
|
#include <doctest.h>
|
||
|
|
||
|
#include <aocpp/Startup.hpp>
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
auto ShortestDistances(std::vector<std::vector<std::uint64_t>> & dist)
|
||
|
{
|
||
|
auto const range = boost::irange(dist.size());
|
||
|
for (auto const k : range) {
|
||
|
for (auto const i : range) {
|
||
|
for (auto const j : range) {
|
||
|
auto const new_dist = dist[i][k] + dist[k][j];
|
||
|
auto & old_dist = dist[i][j];
|
||
|
if (old_dist > new_dist) old_dist = new_dist;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct RawRoom {
|
||
|
std::string name;
|
||
|
std::uint64_t flow;
|
||
|
std::vector<std::string> connections;
|
||
|
};
|
||
|
|
||
|
struct Room {
|
||
|
std::uint64_t flow;
|
||
|
std::vector<std::uint64_t> connections;
|
||
|
};
|
||
|
|
||
|
auto Parse(std::istream & in) -> std::vector<RawRoom>
|
||
|
{
|
||
|
std::vector<RawRoom> result;
|
||
|
std::string line;
|
||
|
while (std::getline(in, line)) {
|
||
|
RawRoom room;
|
||
|
using namespace boost::spirit;
|
||
|
auto const name = qi::as_string[+qi::alpha];
|
||
|
auto const room_description =
|
||
|
"Valve " >>
|
||
|
name [ ([&](auto n) { room.name = n; }) ] >>
|
||
|
" has flow rate=" >>
|
||
|
qi::ulong_long [ ([&](auto f) { room.flow = f; }) ] >>
|
||
|
"; tunnel" >> -qi::string("s") >>
|
||
|
" lead" >> -qi::string("s") >>
|
||
|
" to valve" >> -qi::string("s") >>
|
||
|
" " >>
|
||
|
(name % ", ") [ ([&](auto c) { room.connections = c; }) ];
|
||
|
|
||
|
if (!qi::parse(line.begin(), line.end(), room_description)) {
|
||
|
throw std::runtime_error{"bad input line"};
|
||
|
}
|
||
|
result.emplace_back(std::move(room));
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
auto GenerateGraph(std::vector<RawRoom> const& raw_rooms) -> std::vector<Room>
|
||
|
{
|
||
|
std::vector<Room> result;
|
||
|
std::map<std::string, std::size_t> names;
|
||
|
|
||
|
std::vector<std::vector<std::uint64_t>> distances(
|
||
|
raw_rooms.size(), std::vector<std::uint64_t>(raw_rooms.size(), 100)); // todo max limit
|
||
|
|
||
|
for (auto const i : boost::irange(raw_rooms.size())) {
|
||
|
names[raw_rooms[i].name] = i;
|
||
|
}
|
||
|
|
||
|
for (auto const i : boost::irange(raw_rooms.size())) {
|
||
|
auto & ds = distances[i];
|
||
|
for (auto const& name : raw_rooms[i].connections) {
|
||
|
ds[names[name]] = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ShortestDistances(distances);
|
||
|
|
||
|
for (auto const i : boost::irange(raw_rooms.size())) {
|
||
|
result.push_back({raw_rooms[i].flow, std::move(distances[i])});
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
using Valves = std::bitset<64>;
|
||
|
|
||
|
struct State {
|
||
|
std::uint64_t time;
|
||
|
std::uint64_t flow;
|
||
|
std::size_t location;
|
||
|
Valves valves;
|
||
|
};
|
||
|
|
||
|
auto Routes(std::vector<Room> const& rooms, std::uint64_t time) -> std::map<unsigned long long, std::uint64_t>
|
||
|
{
|
||
|
std::vector<State> states { State{time, 0, 0, {}} };
|
||
|
std::map<unsigned long long, std::uint64_t> result;
|
||
|
|
||
|
while (!states.empty()) {
|
||
|
auto state = states.back();
|
||
|
states.pop_back();
|
||
|
|
||
|
auto const& room = rooms[state.location];
|
||
|
|
||
|
for (auto const i : boost::irange(rooms.size())) {
|
||
|
auto valves = state.valves;
|
||
|
if (valves[i]) { continue; }
|
||
|
auto const flow = rooms[i].flow;
|
||
|
if (flow == 0) { continue; }
|
||
|
auto const cost = room.connections[i];
|
||
|
if (cost >= state.time) { continue; }
|
||
|
auto const time_ = state.time - cost - 1;
|
||
|
|
||
|
valves[i] = true;
|
||
|
auto const flow_ = state.flow + time_;
|
||
|
|
||
|
if (result[valves.to_ullong()] < flow_) {
|
||
|
result[valves.to_ullong()] = flow_;
|
||
|
}
|
||
|
|
||
|
states.push_back({ time_, state.flow + time_, i, valves});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
TEST_SUITE("2022-16 examples") {
|
||
|
TEST_CASE("example") {
|
||
|
std::istringstream in {
|
||
|
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
auto main(int argc, char** argv) -> int
|
||
|
{
|
||
|
auto const input = Parse(*aocpp::Startup(argc, argv));
|
||
|
auto const graph = GenerateGraph(input);
|
||
|
auto const routes = Routes(graph, 30);
|
||
|
|
||
|
std::uint64_t p1 = 0;
|
||
|
for (auto const& kv : routes) {
|
||
|
p1 = std::max(p1, kv.second);
|
||
|
}
|
||
|
|
||
|
std::cout << "Part 1: " << p1 << std::endl;
|
||
|
//Part2(snapshots, std::cout << "Part 2:\n");
|
||
|
}
|