From 3ba00ad3eb2503601cec9bc1542bb45739987028 Mon Sep 17 00:00:00 2001 From: Matthew Gretton-Dann Date: Wed, 12 Jan 2022 11:31:09 +0000 Subject: [PATCH] Separate out Dijkstra implementation This separates the Dijkstra implementation into its own generic function. Whilst doing this we can simplify the code and also save yet more memory! Still don't get correct results for part 2. --- 2021/graph-utils.h | 30 +++++++++++++++++++----------- 2021/puzzle-23-01.cc | 12 ++++++++++++ 2021/puzzle-23-02.cc | 14 ++++++++++++++ 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/2021/graph-utils.h b/2021/graph-utils.h index be60678..250c445 100644 --- a/2021/graph-utils.h +++ b/2021/graph-utils.h @@ -83,10 +83,13 @@ auto dijkstra(Node const& initial, Cost initial_cost, TransitionManager transiti /** \c nodes maintains a map of all the nodes we've visited or want to visit. Nodes that cost * less than the current cost at the front of costs have been visited. The rest haven't. */ - std::map nodes; + std::map, NodePCmp> nodes; /** \c costs maintains a map of costs to the nodes that cost that much to visit. */ std::map> costs; - Cost current_cost{std::numeric_limits::min()}; + Cost current_cost{initial_cost}; + Node const* current_node{new Node(initial)}; + nodes.insert({current_node, {current_cost, nullptr}}); + costs.insert({current_cost, {current_node}}); /* Helper lambda to clean up after ourselves. */ auto cleanup = [](auto& nodes) { @@ -96,38 +99,35 @@ auto dijkstra(Node const& initial, Cost initial_cost, TransitionManager transiti }; /* Helper lambda to insert into the maps. */ - auto inserter = [&costs, &nodes, ¤t_cost](Node const& node, Cost cost) { + auto inserter = [&costs, &nodes, ¤t_node, ¤t_cost](Node const& node, Cost cost) { cost += current_cost; auto node_it{nodes.find(&node)}; /* Skip inserting nodes we've already visited, or that would cost more to visit. */ - if (node_it != nodes.end() && node_it->second <= cost) { + if (node_it != nodes.end() && node_it->second.first <= cost) { return; } Node const* nodep{nullptr}; if (node_it == nodes.end()) { nodep = new Node(node); - nodes.insert({nodep, cost}); + nodes.insert({nodep, {cost, current_node}}); } else { /* Node has a cheaper cost than we thought: Remove the node from its old cost list */ nodep = node_it->first; - auto cost_it{costs.find(node_it->second)}; + auto cost_it{costs.find(node_it->second.first)}; assert(cost_it != costs.end()); cost_it->second.erase(nodep); /* Now update the cost in the nodes map and use the nodep as the node pointer. */ - node_it->second = cost; + node_it->second.first = cost; + node_it->second.second = current_node; } auto [cost_it, success] = costs.insert({cost, {}}); cost_it->second.insert(nodep); }; - Node* init{new Node(initial)}; - nodes.insert({init, initial_cost}); - costs.insert({initial_cost, {init}}); - std::uint64_t iter{0}; while (!costs.empty()) { auto cost_it{costs.begin()}; @@ -137,6 +137,7 @@ auto dijkstra(Node const& initial, Cost initial_cost, TransitionManager transiti return a + c.second.size(); }) == nodes.size() - iter); for (auto& nodep : cost_it->second) { + current_node = nodep; if (iter++ % 100'000 == 0) { std::cout << "Iteration: " << iter << " cost " << current_cost << " total number of nodes: " << nodes.size() @@ -145,6 +146,13 @@ auto dijkstra(Node const& initial, Cost initial_cost, TransitionManager transiti } if (transition_manager.is_finished(*nodep)) { auto result{std::make_pair(*nodep, current_cost)}; + /* Print the backtrace. */ + Node const* n{nodep}; + while (n != nullptr) { + auto node_it{nodes.find(n)}; + std::cout << "Cost: " << node_it->second.first << ":\n" << *n; + n = node_it->second.second; + } cleanup(nodes); return result; } diff --git a/2021/puzzle-23-01.cc b/2021/puzzle-23-01.cc index c4d1715..c92a7e0 100644 --- a/2021/puzzle-23-01.cc +++ b/2021/puzzle-23-01.cc @@ -169,6 +169,18 @@ private: std::array State::finished_ = {'.', '.', '.', '.', '.', '.', '.', 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D'}; +std::ostream& operator<<(std::ostream& os, State const& s) +{ + os << "#############\n" + << '#' << s.node(0) << s.node(1) << '.' << s.node(2) << '.' << s.node(3) << '.' << s.node(4) + << '.' << s.node(5) << s.node(6) << "#\n" + << "###" << s.node(7) << '#' << s.node(8) << '#' << s.node(9) << '#' << s.node(10) << "###\n" + << " #" << s.node(11) << '#' << s.node(12) << '#' << s.node(13) << '#' << s.node(14) << "#\n" + << " #########\n"; + + return os; +} + struct StateTranstitionManager { bool is_finished(State const& state) { return state.finished(); } diff --git a/2021/puzzle-23-02.cc b/2021/puzzle-23-02.cc index c68621a..504295a 100644 --- a/2021/puzzle-23-02.cc +++ b/2021/puzzle-23-02.cc @@ -191,6 +191,20 @@ std::array State::finished_ = {'.', '.', '.', '.', '.', '.', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D'}; + +std::ostream& operator<<(std::ostream& os, State const& s) +{ + os << "#############\n" + << '#' << s.node(0) << s.node(1) << '.' << s.node(2) << '.' << s.node(3) << '.' << s.node(4) + << '.' << s.node(5) << s.node(6) << "#\n" + << "###" << s.node(7) << '#' << s.node(8) << '#' << s.node(9) << '#' << s.node(10) << "###\n" + << " #" << s.node(11) << '#' << s.node(12) << '#' << s.node(13) << '#' << s.node(14) << "#\n" + << " #" << s.node(15) << '#' << s.node(16) << '#' << s.node(17) << '#' << s.node(18) << "#\n" + << " #" << s.node(19) << '#' << s.node(20) << '#' << s.node(21) << '#' << s.node(22) << "#\n" + << " #########\n"; + + return os; +} struct StateTranstitionManager { bool is_finished(State const& state) { return state.finished(); }