From 17a37ccf5a1a47df17d9bb90b09e0bf0d61b0183 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Sat, 14 Dec 2019 15:32:06 +0100 Subject: [PATCH] Optimize dependency order by topological sort. --- 2019/src/day14.cpp | 94 +++++++++++++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 31 deletions(-) diff --git a/2019/src/day14.cpp b/2019/src/day14.cpp index f20c07c..0f5339a 100644 --- a/2019/src/day14.cpp +++ b/2019/src/day14.cpp @@ -30,14 +30,16 @@ namespace { auto requirements_part = line.substr(0, split_point); auto production_part = line.substr(split_point + 4); - for(auto it = std::regex_token_iterator(requirements_part.begin(), requirements_part.end(), listing_regex, {1, 2}); it != std::cregex_token_iterator(); ++it) { + for (auto it = std::regex_token_iterator(requirements_part.begin(), requirements_part.end(), listing_regex, + {1, 2}); it != std::cregex_token_iterator(); ++it) { std::from_chars(it->first, it->second, amount); ++it; requirements.emplace_back(*it, amount); } - for(auto it = std::regex_token_iterator(production_part.begin(), production_part.end(), listing_regex, {1, 2}); it != std::cregex_token_iterator(); ++it) { + for (auto it = std::regex_token_iterator(production_part.begin(), production_part.end(), listing_regex, + {1, 2}); it != std::cregex_token_iterator(); ++it) { std::from_chars(it->first, it->second, amount); ++it; @@ -63,48 +65,78 @@ namespace { return inverted; } - std::int64_t - ore_required(const std::string &element, std::int64_t amount, std::unordered_map &stock, - const std::map &recipes, std::unordered_map inverted) { - if (element == "ORE") { - return amount; + std::vector topological_order(const std::map &recipes) { + std::vector order; + + std::unordered_map> edges; + for (auto &entry : recipes) { + for (auto &production : entry.first) { + std::transform(entry.second.begin(), entry.second.end(), std::back_inserter(edges[production.first]), + [](const auto &x) { + return x.first; + }); + } } - if (stock[element] > 0) { - auto from_stock = std::min(amount, stock[element]); - amount -= from_stock; - stock[element] -= from_stock; + std::unordered_map incoming_edge_count; + for (const auto &entry : edges) { + for (const auto &parent : entry.second) { + incoming_edge_count[parent]++; + } } - auto &productions = inverted.at(element); - auto &requirements = recipes.at(productions); + std::deque childless{"FUEL"}; - auto number_produced = std::find_if(productions.begin(), productions.end(), - [element](const auto &x) { return x.first == element; })->second; + while (!childless.empty()) { + auto current = childless.front(); + childless.pop_front(); + order.emplace_back(current); - auto productions_needed = amount / number_produced + (amount % number_produced ? 1 : 0); - - std::int64_t ore_needed = 0; - - for (auto &requirement : requirements) { - ore_needed += ore_required(requirement.first, requirement.second * productions_needed, stock, recipes, - inverted); + for (const auto &parent : edges[current]) { + if (--incoming_edge_count[parent] == 0) { + childless.push_back(parent); + } + } } - for (auto &production : productions) { - stock[production.first] += productions_needed * production.second; - } - - stock[element] -= amount; - - return ore_needed; + return order; } std::int64_t ore_to_fuel(const std::map &recipes, std::int64_t amount = 1) { auto inverted = element_creators(recipes); - std::unordered_map stock; + auto order = topological_order(recipes); - return ore_required("FUEL", amount, stock, recipes, inverted); + std::unordered_map total_requirements; + total_requirements["FUEL"] = amount; + + for (const auto &element : order) { + if (element == "ORE") { + break; + } + + const auto number_required = total_requirements[element]; + if (number_required <= 0) { + continue; + } + + const auto &productions = inverted.at(element); + const auto &requirements = recipes.at(productions); + + auto number_produced = std::find_if(productions.begin(), productions.end(), + [element](const auto &x) { return x.first == element; })->second; + + auto productions_needed = number_required / number_produced + (number_required % number_produced ? 1 : 0); + + for (auto &requirement : requirements) { + total_requirements[requirement.first] += requirement.second * productions_needed; + } + + for (auto &production : productions) { + total_requirements[production.first] -= productions_needed * production.second; + } + } + + return total_requirements["ORE"]; } }