#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::set perm; std::set temp; std::vector result; auto visit_ = [&](auto& rec, std::string name) -> void { if (!perm.contains(name)) { if (!temp.insert(name).second) throw std::runtime_error{"cyclic recipes"}; if (recipes.contains(name)) { for (auto && [_, c] : recipes.at(name).second) { rec(rec, c); } } temp.erase(name); perm.insert(name); result.push_back(name); } }; auto visit = [&](auto name) { visit_(visit_, name); }; for (auto && [k,_] : recipes) { visit(k); } 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; }