diff options
Diffstat (limited to '2018/src/day07.cpp')
| -rw-r--r-- | 2018/src/day07.cpp | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/2018/src/day07.cpp b/2018/src/day07.cpp new file mode 100644 index 0000000..e6147d6 --- /dev/null +++ b/2018/src/day07.cpp | |||
| @@ -0,0 +1,194 @@ | |||
| 1 | #include <filesystem> | ||
| 2 | #include <fstream> | ||
| 3 | #include <iostream> | ||
| 4 | #include <map> | ||
| 5 | #include <set> | ||
| 6 | #include <sstream> | ||
| 7 | #include <string> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 10 | using namespace std; | ||
| 11 | |||
| 12 | namespace { | ||
| 13 | |||
| 14 | vector<string> | ||
| 15 | split(const string& line) | ||
| 16 | { | ||
| 17 | stringstream strm{ line }; | ||
| 18 | vector<string> words; | ||
| 19 | |||
| 20 | for ( string word; getline(strm, word, ' '); ) { | ||
| 21 | words.emplace_back(word); | ||
| 22 | } | ||
| 23 | |||
| 24 | return words; | ||
| 25 | } | ||
| 26 | |||
| 27 | map<string, set<string>> | ||
| 28 | read_file(const filesystem::path& filename) | ||
| 29 | { | ||
| 30 | ifstream file{ filename }; | ||
| 31 | map<string, set<string>> graph; | ||
| 32 | |||
| 33 | for ( string line; getline(file, line); ) { | ||
| 34 | auto words = split(line); | ||
| 35 | graph[words.at(1)].emplace(words.at(7)); | ||
| 36 | } | ||
| 37 | |||
| 38 | return graph; | ||
| 39 | } | ||
| 40 | |||
| 41 | vector<string> | ||
| 42 | topologicalSort(const map<string, set<string>>& graph) | ||
| 43 | { | ||
| 44 | map<string, int> indegree; | ||
| 45 | |||
| 46 | // Eingangsgrade initialisieren | ||
| 47 | for ( const auto& [node, neighbors]: graph ) { | ||
| 48 | if ( !indegree.contains(node) ) { | ||
| 49 | indegree[node] = 0; | ||
| 50 | } | ||
| 51 | for ( const auto& neighbor: neighbors ) { | ||
| 52 | indegree[neighbor]++; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | // Queue mit Knoten, deren Eingangsgrad 0 ist | ||
| 57 | priority_queue<string, vector<string>, greater<>> queue; | ||
| 58 | for ( const auto& [node, deg]: indegree ) { | ||
| 59 | if ( deg == 0 ) { | ||
| 60 | queue.push(node); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | vector<string> sorted; | ||
| 65 | |||
| 66 | // Kahn's Algorithmus | ||
| 67 | while ( !queue.empty() ) { | ||
| 68 | string node = queue.top(); | ||
| 69 | queue.pop(); | ||
| 70 | |||
| 71 | sorted.push_back(node); | ||
| 72 | |||
| 73 | if ( !graph.contains(node) ) { | ||
| 74 | continue; | ||
| 75 | } | ||
| 76 | |||
| 77 | for ( const auto& neighbor: graph.at(node) ) { | ||
| 78 | indegree[neighbor]--; | ||
| 79 | if ( indegree[neighbor] == 0 ) { | ||
| 80 | queue.push(neighbor); | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | // Prüfen auf Zyklus | ||
| 86 | if ( sorted.size() != indegree.size() ) { | ||
| 87 | throw runtime_error("Zyklus im Graph gefunden! Keine topologische Sortierung möglich."); | ||
| 88 | } | ||
| 89 | |||
| 90 | return sorted; | ||
| 91 | } | ||
| 92 | |||
| 93 | void | ||
| 94 | part1(const map<string, set<string>>& graph) | ||
| 95 | { | ||
| 96 | auto sorted = topologicalSort(graph); | ||
| 97 | |||
| 98 | cout << "Part 1: "; | ||
| 99 | ranges::copy(sorted, ostream_iterator<string>(cout)); | ||
| 100 | cout << '\n'; | ||
| 101 | } | ||
| 102 | |||
| 103 | void | ||
| 104 | part2(const map<string, set<string>>& graph, const int workers, const int base) | ||
| 105 | { | ||
| 106 | map<string, int> indegree; | ||
| 107 | |||
| 108 | // Eingangsgrade initialisieren | ||
| 109 | for ( const auto& [node, neighbors]: graph ) { | ||
| 110 | if ( !indegree.contains(node) ) { | ||
| 111 | indegree[node] = 0; | ||
| 112 | } | ||
| 113 | for ( const auto& neighbor: neighbors ) { | ||
| 114 | indegree[neighbor]++; | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | set<string> available; | ||
| 119 | for ( const auto& [node, deg]: indegree ) { | ||
| 120 | if ( deg == 0 ) { | ||
| 121 | available.insert(node); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | set<pair<int, string>> finishing; | ||
| 126 | |||
| 127 | int active_workers = 0; | ||
| 128 | int time = 0; | ||
| 129 | |||
| 130 | while ( !indegree.empty() ) { | ||
| 131 | while ( !finishing.empty() && finishing.begin()->first == time ) { | ||
| 132 | auto [finish_time, node] = *finishing.begin(); | ||
| 133 | finishing.erase(finishing.begin()); | ||
| 134 | |||
| 135 | active_workers--; | ||
| 136 | |||
| 137 | indegree.erase(node); | ||
| 138 | |||
| 139 | if ( !graph.contains(node) ) { | ||
| 140 | continue; | ||
| 141 | } | ||
| 142 | |||
| 143 | for ( const auto& neighbor: graph.at(node) ) { | ||
| 144 | indegree[neighbor]--; | ||
| 145 | if ( indegree[neighbor] == 0 ) { | ||
| 146 | available.insert(neighbor); | ||
| 147 | } | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | while ( active_workers < workers && !available.empty() ) { | ||
| 152 | auto node = *available.begin(); | ||
| 153 | available.erase(available.begin()); | ||
| 154 | |||
| 155 | int duration = base + (node.at(0) - 'A' + 1); | ||
| 156 | int finish_time = time + duration; | ||
| 157 | |||
| 158 | finishing.emplace(finish_time, node); | ||
| 159 | active_workers++; | ||
| 160 | } | ||
| 161 | |||
| 162 | if ( finishing.empty() ) { | ||
| 163 | if ( available.empty() ) { | ||
| 164 | break; | ||
| 165 | } | ||
| 166 | else { | ||
| 167 | time++; | ||
| 168 | } | ||
| 169 | } | ||
| 170 | else { | ||
| 171 | time = finishing.begin()->first; | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | cout << "Part 2: " << time << '\n'; | ||
| 176 | } | ||
| 177 | |||
| 178 | } // namespace | ||
| 179 | |||
| 180 | int | ||
| 181 | main() | ||
| 182 | { | ||
| 183 | auto graph = read_file("data/day07.txt"); | ||
| 184 | try { | ||
| 185 | part1(graph); | ||
| 186 | |||
| 187 | static const int WORKERS = 5; | ||
| 188 | static const int BASE = 60; | ||
| 189 | part2(graph, WORKERS, BASE); | ||
| 190 | } | ||
| 191 | catch ( exception& e ) { | ||
| 192 | cerr << "Fehler: " << e.what() << '\n'; | ||
| 193 | } | ||
| 194 | } | ||
