#include #include #include #include #include #include #include #include #include using std::cout, std::endl; constexpr const char content[] = { #embed "input.txt" }; enum class Direction { N, S, E, W }; struct Position { size_t x; size_t y; bool operator==(const Position& other) const { return x == other.x && y == other.y; } }; namespace std { template <> struct hash { std::size_t operator()(const Position& p) const { return std::hash{}(p.x) ^ std::hash{}(p.y); } }; } struct Node { int cost; Position pos; int dir; bool operator>(const Node& other) const { return cost > other.cost; } }; std::optional find_shortest_path(Position start, Position end, std::vector& grid) { int rows = grid.size(), cols = grid[0].size(); std::vector>> dist( rows, std::vector>( cols, std::vector(4, std::numeric_limits::max()) ) ); std::array, 4> dirs = {std::pair{0,1}, {1,0}, {0,-1}, {-1,0}}; std::priority_queue, std::greater> pq; for (int d = 0; d < 4; ++d) { dist[start.y][start.x][d] = 0; pq.push({0, {start.y, start.x}, d}); } while (!pq.empty()) { auto [cost, pos, curr_dir] = pq.top(); auto [y, x] = pos; pq.pop(); if (y == end.y && x == end.x) return cost; if (cost > dist[y][x][curr_dir]) continue; for (int d = 0; d < 4; ++d) { auto ny = y + dirs[d].first; auto nx = x + dirs[d].second; if (grid[ny][nx] == '#') continue; auto new_cost = cost + 1; if (curr_dir != d) new_cost += 1000; if (new_cost < dist[ny][nx][d]) { dist[ny][nx][d] = new_cost; pq.push({new_cost, {ny, nx}, d}); } } } return std::nullopt; } int part_1() { std::istringstream input_stream(content); std::string line; Position start; Position end; std::vector grid = {}; size_t line_num = 0; while(std::getline(input_stream, line)) { if(line.contains("S")) start = {line.find("S"), line_num}; if(line.contains("E")) end = {line.find("E"), line_num}; grid.push_back(line); line_num++; } auto result = find_shortest_path(start, end, grid); if(result.has_value()) { return result.value(); } else { return -1; } } struct SittingNode { int cost; std::unordered_set path = {}; Position pos; int dir; bool operator>(const SittingNode& other) const { return cost > other.cost; } }; std::optional find_best_sitting_place(Position start, Position end, std::vector& grid) { std::unordered_set all_short_paths = {}; int rows = grid.size(), cols = grid[0].size(); std::vector>> dist( rows, std::vector>( cols, std::vector(4, std::numeric_limits::max()) ) ); std::array, 4> dirs = {std::pair{0,1}, {1,0}, {0,-1}, {-1,0}}; std::priority_queue, std::greater> pq; for (int d = 0; d < 4; ++d) { dist[start.y][start.x][d] = 0; std::unordered_set path = {start}; pq.push({0, path, {start.y, start.x }, d}); } while (!pq.empty()) { auto [cost, path, pos, curr_dir] = pq.top(); auto [y, x] = pos; pq.pop(); if (y == end.y && x == end.x) { path.insert({y, x}); all_short_paths.merge(path); } if (cost > dist[y][x][curr_dir]) continue; for (int d = 0; d < 4; ++d) { auto ny = y + dirs[d].first; auto nx = x + dirs[d].second; if (grid[ny][nx] == '#') continue; auto new_cost = cost + 1; auto new_path = path; new_path.insert({ny, nx}); if (curr_dir != d) new_cost += 1000; if (new_cost <= dist[ny][nx][d]) { dist[ny][nx][d] = new_cost; pq.push({new_cost, new_path, {ny, nx}, d}); } } } return all_short_paths.size(); } int part_2() { std::istringstream input_stream(content); std::string line; Position start; Position end; std::vector grid = {}; size_t line_num = 0; while(std::getline(input_stream, line)) { if(line.contains("S")) start = {line.find("S"), line_num}; if(line.contains("E")) end = {line.find("E"), line_num}; grid.push_back(line); line_num++; } auto result = find_best_sitting_place(start, end, grid); if(result.has_value()) { return result.value(); } else { return -1; } } int main() { std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); auto p1_val = part_1(); std::chrono::steady_clock::time_point middle = std::chrono::steady_clock::now(); auto p2_val = part_2(); std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); cout << "Part 1: " << p1_val << " (" << std::chrono::duration_cast(middle - begin).count() << "µs)" << endl; cout << "Part 2: " << p2_val << " (" << std::chrono::duration_cast(end - middle).count() << "µs)" << endl; return 0; }