#include #include #include #include #include #include #include #include #include #include #include namespace { using Recipes = std::map>>>; auto Parse(std::istream & in) -> Recipes { Recipes result; std::string line; std::int64_t n; std::string word; while (std::getline(in, line)) { std::vector> components; std::istringstream sin {line}; bool leftSide; do { sin >> n >> word; leftSide = word.back() == ','; if (leftSide) word.pop_back(); components.push_back({n, word}); } while(leftSide); sin >> word; // skip sin >> n >> word; result.insert({word, {n, std::move(components)}}); } return result; } auto Topsort(Recipes const& recipes) -> std::vector { // Kahn's algorithm std::vector result; result.reserve(recipes.size()); std::map marks; // false temp, true done auto const visit = [&](auto const& visit, auto const& name) -> void { auto [mark_iterator, is_new] = marks.try_emplace(name, false); auto& [_, is_done] = *mark_iterator; if (is_new) { auto const rit = recipes.find(name); if (rit != recipes.end()) { for (auto const& [_, c] : rit->second.second) { visit(visit, c); } } is_done = true; result.push_back(name); } else if (!is_done) { throw std::runtime_error{"cyclic graph"}; } }; for (auto const& [c,_] : recipes) { visit(visit, c); } std::reverse(result.begin(), result.end()); return result; } class Machine { struct Entry { std::int64_t need; std::int64_t batch; std::vector> components; }; std::int64_t ore_; std::vector process_; public: Machine(Recipes const& recipes) { auto order = Topsort(recipes); order.pop_back(); process_.resize(order.size(), {}); std::map indexes; for (auto && name : order) { indexes.insert({name, indexes.size()}); } for (auto && [k,v] : recipes) { auto & entry = process_[indexes[k]]; entry.batch = v.first; for (auto && [m,c] : v.second) { entry.components.push_back({m, c == "ORE" ? ore_ : process_[indexes[c]].need}); } } } auto operator()(std::int64_t fuel) { for (auto && entry : process_) { entry.need = 0; } process_[0].need = fuel; ore_ = 0; for (auto && entry : process_) { auto batches = (entry.need + entry.batch - 1) / entry.batch; for (auto && [m,c] : entry.components) { c += batches*m; } } return ore_; } }; auto ComputeFuel(Machine & machine, std::int64_t ore) { std::int64_t tooHi = 1; while (machine(tooHi) <= ore) { tooHi *= 2; } std::int64_t lo = 0; while (lo+1 != tooHi) { auto mid = std::midpoint(lo, tooHi); (machine(mid) > ore ? tooHi : lo) = mid; } return lo; } } // namespace auto main(int argc, char** argv) -> int { auto recipes = Parse(aocpp::Startup(argc, argv)); auto machine = Machine(recipes); auto part1 = machine(1); auto part2 = ComputeFuel(machine, 1'000'000'000'000); std::cout << "Part 1: " << part1 << std::endl; std::cout << "Part 2: " << part2 << std::endl; }