diff --git a/2022/16.cpp b/2022/16.cpp index 44bd9bd..160f41f 100644 --- a/2022/16.cpp +++ b/2022/16.cpp @@ -6,10 +6,16 @@ #include #include #include +#include +#include + #include #include #include +#include +#include +#include #include @@ -47,42 +53,50 @@ auto Parse(std::istream & in) -> std::vector std::vector result; std::string line; while (std::getline(in, line)) { - RawRoom room; - using namespace boost::spirit; + namespace phx = boost::phoenix; + namespace qi = boost::spirit::qi; + using namespace qi::labels; + using It = std::string::const_iterator; + auto const name = qi::as_string[+qi::alpha]; - auto const room_description = + + qi::rule room_description = "Valve " >> - name [ ([&](auto n) { room.name = n; }) ] >> + name [ phx::bind(&RawRoom::name, _val) = _1 ] >> " has flow rate=" >> - qi::ulong_long [ ([&](auto f) { room.flow = f; }) ] >> + qi::ulong_long [ phx::bind(&RawRoom::flow, _val) = _1 ] >> "; tunnel" >> -qi::string("s") >> " lead" >> -qi::string("s") >> " to valve" >> -qi::string("s") >> " " >> - (name % ", ") [ ([&](auto c) { room.connections = c; }) ]; + (name % ", ") [ phx::bind(&RawRoom::connections, _val) = _1 ]; - if (!qi::parse(line.begin(), line.end(), room_description)) { + It b = line.begin(); + It e = line.end(); + result.emplace_back(); + if (!qi::parse(b, e, room_description, result.back())) { throw std::runtime_error{"bad input line"}; } - result.emplace_back(std::move(room)); } return result; } -auto GenerateGraph(std::vector const& raw_rooms) -> std::vector +auto GenerateGraph(std::vector const& raw_rooms) -> std::pair> { - std::vector result; - std::map names; + std::pair> result {}; + std::unordered_map names; std::vector> distances( - raw_rooms.size(), std::vector(raw_rooms.size(), 100)); // todo max limit + raw_rooms.size(), std::vector(raw_rooms.size(), raw_rooms.size())); for (auto const i : boost::irange(raw_rooms.size())) { names[raw_rooms[i].name] = i; + if (raw_rooms[i].name == "AA") result.first = i; } for (auto const i : boost::irange(raw_rooms.size())) { auto & ds = distances[i]; + ds[i] = 0; for (auto const& name : raw_rooms[i].connections) { ds[names[name]] = 1; } @@ -91,7 +105,7 @@ auto GenerateGraph(std::vector const& raw_rooms) -> std::vector ShortestDistances(distances); for (auto const i : boost::irange(raw_rooms.size())) { - result.push_back({raw_rooms[i].flow, std::move(distances[i])}); + result.second.push_back({raw_rooms[i].flow, std::move(distances[i])}); } return result; @@ -106,37 +120,67 @@ struct State { Valves valves; }; -auto Routes(std::vector const& rooms, std::uint64_t time) -> std::map +auto Routes( + std::size_t const start, + std::vector const& rooms, std::uint64_t initial_time +) -> std::unordered_map { - std::vector states { State{time, 0, 0, {}} }; - std::map result; + std::vector states { State{initial_time, 0, start, {}} }; + std::unordered_map result; while (!states.empty()) { - auto state = states.back(); + auto const 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_; + // don't revisit a valve + if (state.valves.test(i)) { continue; } - if (result[valves.to_ullong()] < flow_) { - result[valves.to_ullong()] = flow_; + // don't visit rooms with useless valves + if (rooms[i].flow == 0) { continue; } + + // don't visit rooms we can't get to in time + auto const cost = room.connections[i]; + if (cost+1 >= state.time) { continue; } + + auto const time = state.time - cost - 1; + auto const flow = state.flow + rooms[i].flow * time; + auto valves = state.valves; + valves.set(i); + + // remember the best we've seen for this valve-set + if (result[valves] < flow) { + result[valves] = flow; } - states.push_back({ time_, state.flow + time_, i, valves}); + states.push_back({time, flow, i, valves}); + } + } + + return result; +} + +auto Part1(std::size_t start, std::vector const& rooms) -> std::uint64_t +{ + auto const routes = Routes(start, rooms, 30); + return *boost::range::max_element(routes | boost::adaptors::map_values); +} + +auto Part2(std::size_t start, std::vector const& rooms) -> std::uint64_t +{ + auto const routes = Routes(start, rooms, 26); + auto const end = routes.end(); + std::uint64_t result {0}; + for (auto it1 = routes.begin(); it1 != end; std::advance(it1, 1)) { + for (auto it2 = std::next(it1); it2 != end; std::advance(it2, 1)) { + // only consider pairs that have disjoint sets of valves + if ((it1->first & it2->first).none()) { + result = std::max(result, it1->second + it2->second); + } } } - return result; } @@ -145,22 +189,26 @@ auto Routes(std::vector const& rooms, std::uint64_t time) -> std::map 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"); + auto const [start, rooms] = GenerateGraph(Parse(*aocpp::Startup(argc, argv))); + std::cout << "Part 1: " << Part1(start, rooms) << std::endl; + std::cout << "Part 2: " << Part2(start, rooms) << std::endl; }